From 3f5fd7787c8d640e599e9ddc487799a05285346f Mon Sep 17 00:00:00 2001 From: Fabian Schlenz Date: Wed, 29 Jun 2016 11:00:29 +0200 Subject: [PATCH] Removed libraries. --- lib/tl-api-v23.jar | Bin 415902 -> 0 bytes lib/tl-core-1.1.0.jar | Bin 17342 -> 0 bytes src/com/droidkit/actors/Actor.java | 297 ----- src/com/droidkit/actors/ActorContext.java | 59 - src/com/droidkit/actors/ActorCreator.java | 15 - src/com/droidkit/actors/ActorRef.java | 121 -- src/com/droidkit/actors/ActorScope.java | 128 -- src/com/droidkit/actors/ActorSelection.java | 24 - src/com/droidkit/actors/ActorSystem.java | 129 -- src/com/droidkit/actors/ActorTime.java | 27 - src/com/droidkit/actors/CurrentActor.java | 19 - src/com/droidkit/actors/Props.java | 88 -- src/com/droidkit/actors/ReflectedActor.java | 137 -- .../dispatch/AbstractDispatchQueue.java | 84 -- .../actors/dispatch/AbstractDispatcher.java | 47 - .../droidkit/actors/dispatch/Dispatch.java | 8 - .../actors/dispatch/QueueListener.java | 10 - .../actors/dispatch/RunnableDispatcher.java | 70 - .../actors/dispatch/SimpleDispatchQueue.java | 116 -- .../actors/dispatch/ThreadPoolDispatcher.java | 129 -- .../actors/mailbox/AbsActorDispatcher.java | 131 -- .../actors/mailbox/ActorDispatcher.java | 25 - src/com/droidkit/actors/mailbox/Envelope.java | 54 - src/com/droidkit/actors/mailbox/Mailbox.java | 88 -- .../actors/mailbox/MailboxesQueue.java | 145 --- .../droidkit/actors/messages/DeadLetter.java | 21 - .../actors/messages/NamedMessage.java | 22 - .../droidkit/actors/messages/PoisonPill.java | 13 - .../droidkit/actors/messages/StartActor.java | 13 - .../droidkit/actors/tasks/ActorAskImpl.java | 165 --- .../droidkit/actors/tasks/AskCallback.java | 12 - .../actors/tasks/AskCancelledException.java | 10 - src/com/droidkit/actors/tasks/AskFuture.java | 105 -- .../actors/tasks/AskTimeoutException.java | 9 - src/com/droidkit/actors/tasks/TaskActor.java | 177 --- .../actors/tasks/messages/TaskCancel.java | 18 - .../actors/tasks/messages/TaskError.java | 24 - .../actors/tasks/messages/TaskRequest.java | 18 - .../actors/tasks/messages/TaskResult.java | 25 - .../actors/tasks/messages/TaskTimeout.java | 18 - src/org/telegram/api/engine/ApiCallback.java | 17 - src/org/telegram/api/engine/AppInfo.java | 43 - src/org/telegram/api/engine/GzipRequest.java | 60 - src/org/telegram/api/engine/Logger.java | 39 - .../telegram/api/engine/LoggerInterface.java | 15 - src/org/telegram/api/engine/RpcCallback.java | 15 - .../telegram/api/engine/RpcCallbackEx.java | 13 - src/org/telegram/api/engine/RpcException.java | 56 - src/org/telegram/api/engine/TelegramApi.java | 1129 ----------------- .../telegram/api/engine/TimeoutException.java | 26 - .../api/engine/file/DownloadListener.java | 12 - .../telegram/api/engine/file/Downloader.java | 368 ------ .../api/engine/file/UploadListener.java | 8 - .../telegram/api/engine/file/Uploader.java | 413 ------ .../api/engine/storage/AbsApiState.java | 41 - src/org/telegram/mtproto/CallWrapper.java | 14 - src/org/telegram/mtproto/MTProto.java | 561 -------- src/org/telegram/mtproto/MTProtoCallback.java | 21 - src/org/telegram/mtproto/ServerException.java | 26 - .../mtproto/TransportSecurityException.java | 26 - .../mtproto/backoff/ExponentalBackoff.java | 64 - .../telegram/mtproto/log/LogInterface.java | 15 - src/org/telegram/mtproto/log/Logger.java | 44 - src/org/telegram/mtproto/pq/Authorizer.java | 225 ---- src/org/telegram/mtproto/pq/PqAuth.java | 33 - .../mtproto/schedule/PrepareSchedule.java | 34 - .../mtproto/schedule/PreparedPackage.java | 37 - .../telegram/mtproto/schedule/Scheduller.java | 627 --------- .../mtproto/schedule/SchedullerListener.java | 8 - .../telegram/mtproto/secure/CryptoUtils.java | 277 ---- src/org/telegram/mtproto/secure/Entropy.java | 45 - .../telegram/mtproto/secure/KeyParameter.java | 19 - src/org/telegram/mtproto/secure/Keys.java | 46 - .../mtproto/secure/aes/AESFastEngine.java | 833 ------------ .../mtproto/secure/aes/AESImplementation.java | 16 - .../secure/aes/DefaultAESImplementation.java | 155 --- .../mtproto/secure/pq/PQImplementation.java | 8 - .../telegram/mtproto/secure/pq/PQLopatin.java | 73 -- .../telegram/mtproto/secure/pq/PQSolver.java | 22 - .../mtproto/state/AbsMTProtoState.java | 84 -- .../mtproto/state/ConnectionInfo.java | 37 - src/org/telegram/mtproto/state/KnownSalt.java | 31 - .../mtproto/state/MemoryProtoState.java | 42 - .../telegram/mtproto/time/TimeOverlord.java | 80 -- src/org/telegram/mtproto/tl/MTBadMessage.java | 27 - .../mtproto/tl/MTBadMessageNotification.java | 55 - .../telegram/mtproto/tl/MTBadServerSalt.java | 64 - src/org/telegram/mtproto/tl/MTFutureSalt.java | 71 -- .../telegram/mtproto/tl/MTFutureSalts.java | 80 -- .../telegram/mtproto/tl/MTGetFutureSalts.java | 52 - .../telegram/mtproto/tl/MTInvokeAfter.java | 50 - src/org/telegram/mtproto/tl/MTMessage.java | 98 -- .../mtproto/tl/MTMessageDetailedInfo.java | 78 -- .../mtproto/tl/MTMessagesContainer.java | 74 -- src/org/telegram/mtproto/tl/MTMsgsAck.java | 69 - .../mtproto/tl/MTNeedResendMessage.java | 70 - .../mtproto/tl/MTNewMessageDetailedInfo.java | 71 -- .../mtproto/tl/MTNewSessionCreated.java | 71 -- src/org/telegram/mtproto/tl/MTPing.java | 50 - .../mtproto/tl/MTPingDelayDisconnect.java | 62 - src/org/telegram/mtproto/tl/MTPong.java | 62 - .../telegram/mtproto/tl/MTProtoContext.java | 45 - src/org/telegram/mtproto/tl/MTRpcError.java | 79 -- src/org/telegram/mtproto/tl/MTRpcResult.java | 72 -- .../telegram/mtproto/tl/pq/ClientDhInner.java | 73 -- .../telegram/mtproto/tl/pq/DhGenFailure.java | 28 - src/org/telegram/mtproto/tl/pq/DhGenOk.java | 36 - .../telegram/mtproto/tl/pq/DhGenResult.java | 60 - .../telegram/mtproto/tl/pq/DhGenRetry.java | 28 - src/org/telegram/mtproto/tl/pq/PQInner.java | 89 -- .../telegram/mtproto/tl/pq/ReqDhParams.java | 111 -- src/org/telegram/mtproto/tl/pq/ReqPQ.java | 80 -- .../mtproto/tl/pq/ReqSetDhClientParams.java | 85 -- src/org/telegram/mtproto/tl/pq/ResPQ.java | 95 -- .../mtproto/tl/pq/ServerDhFailure.java | 82 -- .../telegram/mtproto/tl/pq/ServerDhInner.java | 89 -- .../telegram/mtproto/tl/pq/ServerDhOk.java | 70 - .../mtproto/tl/pq/ServerDhParams.java | 12 - .../telegram/mtproto/tl/pq/TLInitContext.java | 26 - .../mtproto/transport/ConnectionType.java | 36 - .../mtproto/transport/PlainTcpConnection.java | 91 -- .../mtproto/transport/TcpContext.java | 652 ---------- .../mtproto/transport/TcpContextCallback.java | 15 - .../mtproto/transport/TransportPool.java | 264 ---- .../transport/TransportPoolCallback.java | 12 - .../mtproto/transport/TransportRate.java | 100 -- .../mtproto/transport/TransportTcpPool.java | 390 ------ src/org/telegram/mtproto/util/BytesCache.java | 103 -- src/org/telegram/mtproto/util/TimeUtil.java | 10 - 129 files changed, 12296 deletions(-) delete mode 100644 lib/tl-api-v23.jar delete mode 100644 lib/tl-core-1.1.0.jar delete mode 100644 src/com/droidkit/actors/Actor.java delete mode 100644 src/com/droidkit/actors/ActorContext.java delete mode 100644 src/com/droidkit/actors/ActorCreator.java delete mode 100644 src/com/droidkit/actors/ActorRef.java delete mode 100644 src/com/droidkit/actors/ActorScope.java delete mode 100644 src/com/droidkit/actors/ActorSelection.java delete mode 100644 src/com/droidkit/actors/ActorSystem.java delete mode 100644 src/com/droidkit/actors/ActorTime.java delete mode 100644 src/com/droidkit/actors/CurrentActor.java delete mode 100644 src/com/droidkit/actors/Props.java delete mode 100644 src/com/droidkit/actors/ReflectedActor.java delete mode 100644 src/com/droidkit/actors/dispatch/AbstractDispatchQueue.java delete mode 100644 src/com/droidkit/actors/dispatch/AbstractDispatcher.java delete mode 100644 src/com/droidkit/actors/dispatch/Dispatch.java delete mode 100644 src/com/droidkit/actors/dispatch/QueueListener.java delete mode 100644 src/com/droidkit/actors/dispatch/RunnableDispatcher.java delete mode 100644 src/com/droidkit/actors/dispatch/SimpleDispatchQueue.java delete mode 100644 src/com/droidkit/actors/dispatch/ThreadPoolDispatcher.java delete mode 100644 src/com/droidkit/actors/mailbox/AbsActorDispatcher.java delete mode 100644 src/com/droidkit/actors/mailbox/ActorDispatcher.java delete mode 100644 src/com/droidkit/actors/mailbox/Envelope.java delete mode 100644 src/com/droidkit/actors/mailbox/Mailbox.java delete mode 100644 src/com/droidkit/actors/mailbox/MailboxesQueue.java delete mode 100644 src/com/droidkit/actors/messages/DeadLetter.java delete mode 100644 src/com/droidkit/actors/messages/NamedMessage.java delete mode 100644 src/com/droidkit/actors/messages/PoisonPill.java delete mode 100644 src/com/droidkit/actors/messages/StartActor.java delete mode 100644 src/com/droidkit/actors/tasks/ActorAskImpl.java delete mode 100644 src/com/droidkit/actors/tasks/AskCallback.java delete mode 100644 src/com/droidkit/actors/tasks/AskCancelledException.java delete mode 100644 src/com/droidkit/actors/tasks/AskFuture.java delete mode 100644 src/com/droidkit/actors/tasks/AskTimeoutException.java delete mode 100644 src/com/droidkit/actors/tasks/TaskActor.java delete mode 100644 src/com/droidkit/actors/tasks/messages/TaskCancel.java delete mode 100644 src/com/droidkit/actors/tasks/messages/TaskError.java delete mode 100644 src/com/droidkit/actors/tasks/messages/TaskRequest.java delete mode 100644 src/com/droidkit/actors/tasks/messages/TaskResult.java delete mode 100644 src/com/droidkit/actors/tasks/messages/TaskTimeout.java delete mode 100644 src/org/telegram/api/engine/ApiCallback.java delete mode 100644 src/org/telegram/api/engine/AppInfo.java delete mode 100644 src/org/telegram/api/engine/GzipRequest.java delete mode 100644 src/org/telegram/api/engine/Logger.java delete mode 100644 src/org/telegram/api/engine/LoggerInterface.java delete mode 100644 src/org/telegram/api/engine/RpcCallback.java delete mode 100644 src/org/telegram/api/engine/RpcCallbackEx.java delete mode 100644 src/org/telegram/api/engine/RpcException.java delete mode 100644 src/org/telegram/api/engine/TelegramApi.java delete mode 100644 src/org/telegram/api/engine/TimeoutException.java delete mode 100644 src/org/telegram/api/engine/file/DownloadListener.java delete mode 100644 src/org/telegram/api/engine/file/Downloader.java delete mode 100644 src/org/telegram/api/engine/file/UploadListener.java delete mode 100644 src/org/telegram/api/engine/file/Uploader.java delete mode 100644 src/org/telegram/api/engine/storage/AbsApiState.java delete mode 100644 src/org/telegram/mtproto/CallWrapper.java delete mode 100644 src/org/telegram/mtproto/MTProto.java delete mode 100644 src/org/telegram/mtproto/MTProtoCallback.java delete mode 100644 src/org/telegram/mtproto/ServerException.java delete mode 100644 src/org/telegram/mtproto/TransportSecurityException.java delete mode 100644 src/org/telegram/mtproto/backoff/ExponentalBackoff.java delete mode 100644 src/org/telegram/mtproto/log/LogInterface.java delete mode 100644 src/org/telegram/mtproto/log/Logger.java delete mode 100644 src/org/telegram/mtproto/pq/Authorizer.java delete mode 100644 src/org/telegram/mtproto/pq/PqAuth.java delete mode 100644 src/org/telegram/mtproto/schedule/PrepareSchedule.java delete mode 100644 src/org/telegram/mtproto/schedule/PreparedPackage.java delete mode 100644 src/org/telegram/mtproto/schedule/Scheduller.java delete mode 100644 src/org/telegram/mtproto/schedule/SchedullerListener.java delete mode 100644 src/org/telegram/mtproto/secure/CryptoUtils.java delete mode 100644 src/org/telegram/mtproto/secure/Entropy.java delete mode 100644 src/org/telegram/mtproto/secure/KeyParameter.java delete mode 100644 src/org/telegram/mtproto/secure/Keys.java delete mode 100644 src/org/telegram/mtproto/secure/aes/AESFastEngine.java delete mode 100644 src/org/telegram/mtproto/secure/aes/AESImplementation.java delete mode 100644 src/org/telegram/mtproto/secure/aes/DefaultAESImplementation.java delete mode 100644 src/org/telegram/mtproto/secure/pq/PQImplementation.java delete mode 100644 src/org/telegram/mtproto/secure/pq/PQLopatin.java delete mode 100644 src/org/telegram/mtproto/secure/pq/PQSolver.java delete mode 100644 src/org/telegram/mtproto/state/AbsMTProtoState.java delete mode 100644 src/org/telegram/mtproto/state/ConnectionInfo.java delete mode 100644 src/org/telegram/mtproto/state/KnownSalt.java delete mode 100644 src/org/telegram/mtproto/state/MemoryProtoState.java delete mode 100644 src/org/telegram/mtproto/time/TimeOverlord.java delete mode 100644 src/org/telegram/mtproto/tl/MTBadMessage.java delete mode 100644 src/org/telegram/mtproto/tl/MTBadMessageNotification.java delete mode 100644 src/org/telegram/mtproto/tl/MTBadServerSalt.java delete mode 100644 src/org/telegram/mtproto/tl/MTFutureSalt.java delete mode 100644 src/org/telegram/mtproto/tl/MTFutureSalts.java delete mode 100644 src/org/telegram/mtproto/tl/MTGetFutureSalts.java delete mode 100644 src/org/telegram/mtproto/tl/MTInvokeAfter.java delete mode 100644 src/org/telegram/mtproto/tl/MTMessage.java delete mode 100644 src/org/telegram/mtproto/tl/MTMessageDetailedInfo.java delete mode 100644 src/org/telegram/mtproto/tl/MTMessagesContainer.java delete mode 100644 src/org/telegram/mtproto/tl/MTMsgsAck.java delete mode 100644 src/org/telegram/mtproto/tl/MTNeedResendMessage.java delete mode 100644 src/org/telegram/mtproto/tl/MTNewMessageDetailedInfo.java delete mode 100644 src/org/telegram/mtproto/tl/MTNewSessionCreated.java delete mode 100644 src/org/telegram/mtproto/tl/MTPing.java delete mode 100644 src/org/telegram/mtproto/tl/MTPingDelayDisconnect.java delete mode 100644 src/org/telegram/mtproto/tl/MTPong.java delete mode 100644 src/org/telegram/mtproto/tl/MTProtoContext.java delete mode 100644 src/org/telegram/mtproto/tl/MTRpcError.java delete mode 100644 src/org/telegram/mtproto/tl/MTRpcResult.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ClientDhInner.java delete mode 100644 src/org/telegram/mtproto/tl/pq/DhGenFailure.java delete mode 100644 src/org/telegram/mtproto/tl/pq/DhGenOk.java delete mode 100644 src/org/telegram/mtproto/tl/pq/DhGenResult.java delete mode 100644 src/org/telegram/mtproto/tl/pq/DhGenRetry.java delete mode 100644 src/org/telegram/mtproto/tl/pq/PQInner.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ReqDhParams.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ReqPQ.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ReqSetDhClientParams.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ResPQ.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ServerDhFailure.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ServerDhInner.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ServerDhOk.java delete mode 100644 src/org/telegram/mtproto/tl/pq/ServerDhParams.java delete mode 100644 src/org/telegram/mtproto/tl/pq/TLInitContext.java delete mode 100644 src/org/telegram/mtproto/transport/ConnectionType.java delete mode 100644 src/org/telegram/mtproto/transport/PlainTcpConnection.java delete mode 100644 src/org/telegram/mtproto/transport/TcpContext.java delete mode 100644 src/org/telegram/mtproto/transport/TcpContextCallback.java delete mode 100644 src/org/telegram/mtproto/transport/TransportPool.java delete mode 100644 src/org/telegram/mtproto/transport/TransportPoolCallback.java delete mode 100644 src/org/telegram/mtproto/transport/TransportRate.java delete mode 100644 src/org/telegram/mtproto/transport/TransportTcpPool.java delete mode 100644 src/org/telegram/mtproto/util/BytesCache.java delete mode 100644 src/org/telegram/mtproto/util/TimeUtil.java diff --git a/lib/tl-api-v23.jar b/lib/tl-api-v23.jar deleted file mode 100644 index 3d6dede49b8cb75c7b3bfb42940fc5cf2efbba73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 415902 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�H_zl`FViulke~z8#iMozbp2FA{rW$E-#G_p>vA~Fx5~E zLBL@ZBxm1G^3Ao->U}-t`yg7&AH2+g$o|@MX}(UJL^91FyUgEb)NL5MU}2ZRe{#mf4W3WzvY;<#>fxe9O#@~1p2I__h|PLJw%UQcLrEMqZ?r2=^sQk2?H|m zzxo;ed!yp-X4QWo0YNhdy?^6@W#BFs8rSE;QR9VZwQ;SDNbyQk{`bPThmn=2u^Uzq zO_qq0eNT)Z?B8iG2&Ki<>Xjtr$bw6P5>_6|~fBEA7qb8pk^sR8CT zmb1eg!Cd+5{w?vntYusPjaeu8kgeg?&KFrRqrc9X0wwqB<$^vU7qt;)Sn`CQ;3aRGdhI zzg13Ks1X_*kfB|ub~@zn0U3b{)Vk1a9qW$QV+Z|Tr>``6rGRs4CGbS#oe@!*XHbrl;Jb&YT@z>1rXGs2?4x}XB$pN2B;~A2Vv=rtf z2&UzQIp}b5knpr&;H($;p$*i4{x7yOyOIF&Bjr?{2fj?PBT1U zPw$VsW#(fJ(OsiH}OB|V=dwv!VjaLKNCrUfdiihvD}!L~SLfX>$OvK8H*XqP*V z=8y?7!Xp4bz{9EzQcIgDogYmHu?`=SYQiFfQK_G@Tr{3yg(fnoUoM9lwc8~;06 z{x4|y&(Qn}FZ@0(D_9(?vo5kP!na4H2~<|5UO)?hK*334Z13An1RSgOQZ5_240NOZ zi~(xJ;Bw!AJjnr_0+Qt+1Cmp}IqW=+xU?}m?!Jm>B*~8O_F7_)4Vm>MABepdN@&?m zjk-q~Lds&;OI%pk*|B!Y1o{M2BR6l8ugG&O9e@W3_990O(gFwA1w{cwX)voSiAL7b z#f}nFiN~e1gepc7#j^l)J?CIN?JCTGkEQcY*2lIY7hDpSZcy7(B4QDjyXbBj2-_S$ zW_SQHgI3cLkQ=OI-X^AyYZkRrq%T2l8>jIrQJyqS2hoJGPwty?^hu)_SD1;ozgegEPjy0)0xWJ)1sU0C@8 zXch4BC7!FI*ujdM0YHAbWCrP@rp}^+K-Xi`ZAws%GYh^5 zIXzRj zRxv1Y_XUvBU}VQ>`*NNH-R)O?VgBlMOjangeG>bi?ZpH4=v>tH7~6I)t5aQ3XP8G7 zS|z&(5}`2CNgXg(qf+V`=1Rkf-9Gt;j>hCk?RCu5d%=>a@SSu;%1nGa=|07neR{KZ z(NE6wt`Q_%W5f4Iflxg}9$t4gSih}qU5x9=Uxi`@|M3psZ>T}R$lCCab8G=UYXc+8 z-{x(<7ZYBEsX*wE0!NQ)oJ-4iYROM1wdPRtP~_pr`O#Sd?U?c$8zS~9u6H+@B}lItRFzxLNg%esz+ zm?BYpQZ(yKK&N*b272%CLpTW9rj_C0ofcL(mGEKEu^Fc767+xg+?|!`kPYF%mqwJe z9!G-Sd^MMN_MWd#$+M%@7~(cHtl>yVIImcOlxXP==y()O5=o zP9aGZZz)=&GJq%Qh|2OYGr)3jb)-FkTlg8xKbHw|EBt`TLnJ4%@V&6snofQzjtNOfHWgE^TK_iazTj%Q&pu zznn@47X3gNfZ6AtpQJ4RxuN^lfSB6YI2irG3V)3Yuu>531S;dz?-R_-h?Ip5P6U>v ze+k3~-hZ(KHi+*l6Yr-8>lYXbfA5g7%ckJ#f<4Oxu9gXU`T{aMc8Az*KkjT_pQH1B zSTsVa)B|IY84zv_7)GcARtnc%KP_$fzr4*pC|1ObO^X#RC(L$5Dy(87RxP^;iE)`u znNKU^kBtyS3CE4?bXj9z2xZFNETXiJkTgsPl@O6MFxk7O$Nnr{CPcU#DzAQlLBKx= zD^)u2F&j&4lbHXo-L)<}cTV`8ID9fh)~2=<%8n{SFfW$D!~>Z!TXJsNXT;hW7N!&T zC=2oCCw4^P&$ayz5Ajyzf*@`$+Mg=@@x_6~!uD&iAm3wToCYiw^iJP8@-Qf(Q8*wd zl@2v1l~MsB1KW$Yt-tq`!QQi*uWrpO)7by^Bwj1zw_^r|2Js(>;cqzmFQEa-hW+VD z{I7v|1q@n7#o|J3-OH5uxoX&)yIR;6ej!o{Q9UlJeWeNJataIl^gW3?q!q3`v9mRh zJK2p{9Kk0Lh#}9^AwmJ|)hwMiM_<%=VM?TxR#zIoZdE1qvIFrVjh5vc4L(Jb;8_-d zJa8jqPX*!dp(^YcuicFCF`%?Uq%;YH3rS2TuL_N@n&mzBx7{kdns=GPS&5+$@^<^7 zC3AHRSwzTI)zoH9WbFZFl zO`%q+ixVautEdS>Nh{)M*&e1%5>zRp|J@%@g1>A`Car~0YVmu)LmYsJWwjnhG* z2Tb_@*x^|8epPc5>e)~fga}8pf*Ab9YlUJvkorjDyxZC4cfrzpc^3ZrM6c*k6Y&)= z(ElH!B5!11V{iCN;`l#D#d&t&L7`-$3aRS4+BEhfc}KN2O&3cu^zi(Zr}7nP;7`8YELU zBUtl*yJ2M7=Bp0L+>08=vV;a)39I+jNRW1GK0+uIqe|(}3t>?SWQH;l1J9h+{InQY z^vsS%rmWGSNa5?IaadS#{GaU#xlZ7ge3F1B-Z5uE2MVS;g4k}CRd4j5gyw>CtP>wSHKZ0x|O6P7Q%fDp;Ub-tm3l)Kzj|(K(t4_ClVvaX!33(hvyv4sW4SC{lv8gJ{myuBk=AF zk-RN#fz}K^N!z|-C@ZGp2rr_)YfvF) z|6F4)bpQ)EKlP?SIg`X4JWdg1l;Dt(jbT8X7C+f~k4);!v+GkF)dmJ~nq*&qtVNw4 zgL8LFLVs8K*JXNRIeg1K9&78$n-l4!{S3GkU}!8~V)me~3&*-b*)MJk-gdAX%hnt% zXpKWL_zE<5bNT*< z!CT_`ssHO6_n$-a-)Udb&DPA?85eT3Y>K}!K+Pi1b02?Wp0PN<6~=EqQSza;(E{WY){d|M|@s=Mr#xG8iXfk2;5TV`4RO!gty~sl-Z#NL2#T54atm`aqzNfC2dnf4 zQfhmJI}QWkf$H_MX+%$!3FIh`o2sNfrU7x-K%@|hrp_$8KKw#T>TjfUP&jw~LP}94 zJ&GLu(JNBUm@z+A-+;A0nvivzz(V+Z;R!UBo{Fuv zmr99x11tt7dFS=rh;#h5i|YKQ-M(JTnj*s7f+y3gv(fJ=VXrK3cz2EDfjfLVnLkWxw+rYbsEF7m8#9w)2J{PgO#FIwgcUV zdT(VswBFAckT#2(cjlv?xssz@1jj}U!TV2}!k|@`LyDHUns;r!mBHtZ5c-L|HzGpRzN+XXda7tYvj81ZPh{gvE3Q{W z)9a%w)UZz$*usVK?$1ma97`=aMnI7@G~N47Q&B(C0QZ5axZ&}qIbe||fL-93jKv`* zi1L~CM`a)eGO$=5yd(_nUlV7#-iJsW*HEojtNOYbcv$6h(xz9%&@A7L&K=e z*Lpfa+v&?qkKIu$ukl%ExHV!2#s}s9Bm4i3&#yt~zXs=z{?=~~(Wy+)yk*OcL{eW% zmX7!BY4zv(Fo!*L1RM&d6C8iEw_uT_$l;&)02!CVsSd_4mlqFLfG>fW@9gJltf7{< zFcUfJF6M}V+3fN;`~|}IAs~(dF`sQ*AgTgPuLzSUmYZ1hx+f;a$w+sqnxq#CmMzj1 zDZCxX{#>C*+57#Bjx`}dDHn;IlYQu}ZK8e(*+{_~o){7^BgFE9E2<*&`*4uiW7b3B z*Z%Vo1tDem6_cb85Q*-c&0Cnx`qKO+<<4QM!oHOiFAa?|);M++ONM@zqw)d%I0&t= z4@hYoazZnnl)Q7@2p2(C-v(2DlfQmKnjU=yhbJi4&^saj?#2)VWs?Pr8C2f`eBDF_ zmtW)$vJE8vmp{p00F4p%zXRw0HlzIm`G05`Ps%o#C$m2p@0R!J`EIkz0ms)&` zup>)oS>orFFa@r82+bS#YD^TH;3g=`9}78><;JQ$del z=3A!#cI$9>Z{vCUbsLEU2`34FoOKawxh z%lII;2K3T%#^RB5PJX29sYL2z2+H!bYgAlNr=YE53TkVyV&ktDRAYf<#lGayKto>C z9}DAn$j+j3DoG*>ZPJ@YLElhnwy*4^TBp^dS-4ggwvC@Xc+eJ3NgdW7uQNB&hIh`S z7D)yS!i}OQY)PhOQ`o39cGXfesm&Y)wOL(Aq3=nyFo4@%aOSnyz!`^v9$!2JY0ndl zSyn5lnBc{6v>|g+Rypm!hhm_*DY#_R7MH}kPe^E#yd1A@NvaaKXMRf-<`)!OI1#8Q zBj-bTHzC&1^2Mdtp*fw1 zcirVHU5N;C8x0^W8m+JMh1{lN^!=TMvMdIELXhMc!8(0(0190f`3g}`C=57bcE=4y zDbRy}{DJO^KAbv$MzN)7m%B7mJ!w8_SN~iFiMcuUQ7FF@ zLo@~Amez6WL(7Ej0UGm*4=DR6T>L=! z4Gj~E@&#O!G|YTTH$S6LpWP*P?l$=zg-K=qo!}zBY)K4N1!ak(4ZKWF#r%u?)Z(?0 zu-&9mf5?V1crdKW<6G0Az^ zC?)@tOCc}2*RDeWx`W15{}{Il9BTV7G7p|)~yw|W;o03_7v08 zuOW*VsX^PTo3+iq2qgV`+Zg*l!%ETK>5s*7r@v?;72#B=b0ul3)39wRl81Fk;I3t7 ztIRW7NU9-<)xD6zx+8GjfwVkuJV9-Iqw*71OQ{EHX$%{zaswy1uBI%qomqA_=iHTJ z^i?(+pLFln?@b+%HQ`8c8J{^RYWuEKzcvz1$p9Spj|_(a_ljSuNag9| z=pWvn?5EcdVgh8* zdB1#^`U?S&_v?Vazi-mh{pbI-lC7bhqtUC5hOC}FFrk^*>RCJT8UBv}+@Un09Ppy5 z`MAyrYC!5lz9Ju7=m#dQI^+Sd6yy3u{l^6S{P-CV8hIBCrb5L8v3h!Kh{}2XM{UM3T-7j{7W|l$WzN znSe*t>_DCCc>`IX3YgJt@&WQMbE8`;2N4W*#1BAoBb8zn3pc$5&#{T5^-lTb1*8j0PTSmOxd`$0 z6mLYE^h%AnuI6HO8e8f-)W2c&upeXM0VmWN;%%+WjUL9a1)kxHGanpswY^4Z)2Ldv zML2DyFDuk@#t{~C%q}{WittX{={jw%u>Drp%Tu8F`pSc#gW8JceZM1ZU`nUBw;gsA z6imiLU8=ec3B-y`?T7h<>OJ9rPr1t#VhtA20gO#NY127sTfR3J8@%yne~6i}Q%j?m zxFRUa{%U}z!Jf4VCQvYKgkVj14mP=@qiLA(9Q{^5C8R-X&gB_2$k%zfVxi0^<)C!h z23)C{f-=2;^}Jk6#)fq&rK)uNS%@jDc%?RW&n1;E#`o#@9bE*!2nL|F=70~Y*b=R~ z_$YO^0<>-Qvehy9LEJe+nWc{*H2x71=mMNZP~G%Bv;^(^=6y(yjH>`4-Gu=?9*ehM z)>0Ql!qOJR)KZMQpK3_;(JDaJka0&Wahz3%OPi4|c0(v0smPH#(BiEIaZKS=FOPGG z!oo(u;%ujR+m}KHyi#KSL%t49vl#LXZKCW8J)inAPU$f-*e7Gc)QQX^WyVx{`0B-2 zLvxpEj&Z{Sp}y)^!>SWQhH=BH@R)5{2^0ERaC?S}VdL+7@yCD`jJuSp%*s?|%l9sH zA|{tBDGXct&7VBpwllkR6W}HUV5zQB_^N>T2;bIxwvEyqFX0yO$s7cAO*-KI_{3D+ zFQe6i?DMOwWBEO*1`C+z{Qfb~{f#;Ne}tYOP~^t(&%+u84TVWrAo|48>)tmKY!kE8 zQi;QZ%ALuF!J>13F!1VdWJeN7i*odn<$Q1dyx~96w|BP&TrOfe5#xtsayr{SG9L9_ z^=>u+8jD4L1`1?l@t1((2_8Gx7sy2GxIQJD^zjEs9 zfwG#5N)^3o|6!tS=1&0Y-k9tQ_Hgb8`?*s6U zJ5VFGV^&(P#wE}TtW$9!j)jl81lSeaOFkdCeT!YSIyVkKb?69uDQNcm_l_-z$zyjH$rtBV~uWv3U6&-RcM4&#Rtb&Y{}x!TS+H z>VxKLTfV7(_v2&ipkXi=E{k1M*3uSR-qIA?7BlzmE~x8zBv?i#p&}8?dSpC_oa4Lr zw~v;%A{Y@~2XF*|`UM?Vbq^JL`=B+`GMmnCRC5M$oR<_#kmfN+O5a;+mNrlrU7>E?X6hB}G@j`PkD+ojho$L`Qc#V-zPcSk`- zVi3*E`&!tEQ?1%XV526lmv^ozq}EL@(4=pDa)hO*!%#`?uWgrBz9rQ-dK{%YrrttT zmNt!_JQ%~0>D(F9ffC8gL+3h3?>sy#P;cM0eG?du2`JiCo{XbGOYqeDjHhOI(8Z$9 z4)p1}tm}rgsyd`XiWfnNP2T9cRd=Em0{EEeQfa$Cf2ohL6zB^Z)~Z*(MV8-Zv$wj% zBHw}XpG|joTo)+hgLP7oZLGW2Zm~Z&MI*l4q2t*O+D6mBoCM+^aZ08ytMP8im?Y& zr9lVL9i+<`UmACUw_sw}H)QkmIw8w2a&{L7F>xBGqLp?77&7OYX}{OisZ&=VD(I~} zcG6;hW?~(o#b7d^IA9^$v*8^hBDaxIXRbQ10ZM*1@64=MeHHUTO7=hjWhrk1#ZbGZ z4!!?**}_h-vzHu`tt0p@zHbAAH1lTx?ml$z5#zn7at+MnA}4UnoV~$PIchj2B3XjM zN*6}An%wCMu1Z`DdaMR3OX($)6P^Z+k2mm*m^43mjo3Qyu8}sIKF9M&qEK+3*zPf| zjYib7Nq$u^&xUL&Icz#9tR|lp+q~HyJ%aSoD$V8y_wHWayEvPHYqyCt+N8jT;j6we<1-T9W+G*Is%x zKO*?Yul;W}QUCXAe_f{d-F5!6{#=v?ZrX8mzDKbo2>T=q9>T)!CoCu@IF4ph(yrRQ zorn=fS6*u^KM$eJ-EDKmSCUubS?PJB@B}`XH7|v4XVBg$eS72h*`c7&1$aDlgZ<-O zn19IT!y8JjU;vDDSHhn7s;&$yIH}NF0P;#PrUlNI-~eO%OKab zgZnrS`}k()_AwRr_Ixek!u9YbXIC+HrD(l6*)o-Cpz@jgZbfy=j^Qj+qZQDUXIp9g zu?63r-6=K9nmvm^1ubIgh**G9hYIn|Hd}q;gt&ska}oK_Je%vj9Eu*v&}F8Q&dzip8)fk6B@T zZF52kqhLQ7weYhZ00OHA4lQa-1J<34-=)GONI(LScP0l516n#B=7;wn(9YvFnym!wo>B><(ch0u*_Aw946 z%N~++;NE05mO;aq4g-@&?hNux1$wn?r1<9I93?){<-b zjBrARrufWgVul+5W`9LcwIc(SYPADL5;fydDZ$uC(=))1sm%0F$3lR)!48Gq7=!>O zfrWDik-9B}Ca!rq^f2iMHgVCW;B449}eCaM%>XShWM;h}91IV{3$1j`~k z1M}J-EMFt{eUbVWV)0XQID15ysqp>phh1A_<)R4iVaN23k~ZZ3SX0?pJL(xYN}5^! z*&b2?KIeFmUXqgD(|&oEgAD9MB=`w;bF5T`Df@>|-Stsgm5#^I4$mygoRQoe<-)QP zBoxLBI zdVt3UeRwp%%@`+UDf=B>(6Ee1XiXP8j#Ukc4TC@Ss#s1N+t$iex1^VWOG92I%3sIy zw=>O}E6}7dQ3W(u^Qe=20~1@gXD1B= zq(h7KRvTC)^U0TQ+RroV$u~rUnlQWbeEW8=gMV{R_ufF{*;U+w44v8K=ynFX;vxz! zlS$`dcBzufpqyIyFo)C}MMtiROs5PzD6Loa=CqYcqokvNc=jwt&!s!;Tt@f_jK$Bo zjtYyOB7MM}BH^N~0N32#9TW=29tiXP=`62GYHVLx-+m_?@A3;*!H--ehPnGn*`CR~u~J>yY0E(4*=dF416^ zAP6#Xge469JT01;iOGib#PNEF7d2qH`J_j~<@6=q`77TP z<^epDLi$*#0^k}p(##zrKXrU5^%3SK5hbCXHiVfjLcrXH2PXIviC|Wqo9sLLPK&)h zsZzXHj@BT`P@upIf*n8>JiS+$z@l6%-P3SHuhJJG1e zPVuP&yM&6>`IDGLD7C6pdPuy#JhHeO4h9@*r9VZ1ehT6Z0;MidFtG(w33bh#Vv@Al z$KuK=zaIIm**y)Gm+uAyG|4|6h5gNx_Ycbb4QP2I6Eg=#Bm4h#NcyAeYt?L}R=Q0@ zgDmK%5knpl0*6TIJ(mX&%GnlY<|02!xmds<1oQ&qLkz9x!dZ)^UzyiZCkw}u+FfaM zIAwD=nZJMD?WK7`F*k%4E}~{wnU_?}yn27n72NBZle7&`cI2F{8i*-UtiQR5D#d

nO9HPzjDP>0x~?o_@mLzQWoA2i*`XI0#MPU7r;O4rwH{i#WB^I+y#c&GMi8Bd4ifa19}cyQ z8ZmXQK`~_!Ns5(9Z1eLX4qgE*6GrQz74}p8m@C(g;%jbrS^S03DM--l0ia&tNX!`} zp2&i7mD{4RMbw8=NG;GXAEJXP?tNr1+jJDae5Kv26SGINSIZH&|5Z2i4#9AeucVc_ zx5Ko4kkR^sR-9#(;li|`%VvMgVa)K@8w9>%cSQ|Z7L7ZUIPyhUg}n|WTbjLd?=RGy zoX{$!e=GvN zJSYBf$MJ8}KYqhN&_Kr4(agsBUkWTVu1TuaMr1+W6MF?HA(c|_xuB3TSgEu#*}dFX zmx$r&#Up9>CwT5h0?!yhg7fxQ0C;=sC(TfI1&<7QCtqed7*DMFcmqcs0(gO-S%mPx z_+IXh^U~{N%o*Ei?;3&j5LNv#q{=tN)4BGczKQ4)LX(w&<{@dt^=tywa;r8Fk$!+l zXr&q|^92Yh$}8ikB;les9{leTtW|!-ruxZz z1En))IYQs~LUNJ6l^-xoPfQtP;;=$(cX33TmH|3AEtwSCIr^C2jv96BbHvMwsN_js zjd0jF8KZQ_Sx@Z1nAyusDJ@d7Xa;UINM_5sTv!@kv!`Fq=Gic-)Oi>~XSye6)UiQK z*G50p&a;}N3@EOY6c*>C4tQ(SEaZ?=mS;6U>eqQVQ(~wJV60|LH?rVIp#nOgSAstu zZX`!qAHLDM!T{qzJlQ(;l>(eMsdUjgK)-8s)RT`kW<>Al6GD|#i7eO1%XwmJEs9hb4Nb%pFRxh3RR*$To4ZwVY-lFdBFAF=Qubg%YxIz zlUrIPx?V4J5wdDYj$AL(;}3IS*nNcRMl>OG5zSTblCs|>WplXrJqFBF2fanMI>4Np zh+867w=+%}b4fr1N$9a+QgbF5jXlZ3xslBjA0|WuAw!1rM@S}&UZ7(u@P=C1E|HpX zNL{xnLZA(Cpyqlj$9h*nuc~#r;-Z_@bBVFp=OqN2jop9rzhMe=PyHN8-u@qjBh7Gb(-PDaVrlSD~9dzr(RGc$Ri5?fwM{>tyt=&t<0cI!Cg*9mOMrpqpxo7M|IvLow56IQ{LeF5%qFj_(*>q6*@y3 zjB?vZDU4Es*i-{A!7HT;a0oma8Szar(Zvv8G*Pn5R_SW9o?+v{v0I!OTry4}RjsuL z4*ZfW9^`-nzhysR-xku+1#Cd5C+yb~JiQvLiszS?4vlc<#rWabh zW9hCYvcebm<=RZ(t`+JNV55`yYZ_Ug8!2L%Wc&`K9X4d=m*qk}g2y zOHBY2-?UJto95t|%5lUhzfgGvWuMR~ZtRG~ss!?FLann2ly)tiC8yn~=cg{@!!5jR zmkBUzn`a8eQ*`u4>nZ2`0)~LHjh0B?iOZzsm+GFMVP~U){1r_WPT;#AYYtv1g{yBe zWc8eEjDU7KvG(}QbhBkwX9(4mhD*0i0xxhu!Q66nN>7LJ&F z$VU*Jgj0LLX?S}!9qbb&VDR6jeOUnx}BOXIjwdu9Bb zXJJnkmEmkNme^4W@8K|3n<1TO3)WevEbTKp1@yGHJaK#RJBZ_+Ud1R-vOyxD5olGD zi9uO(r_gTL`J~q<>oZK#Xx;c=r{nxsjpyN~qxj5EMiYXLQt%n?t%!=%cp{2h4zEcH zT{q~@mylIk;j#0Xe5uU%DtXQRFBiu}xaO_cANBiw`ukE#H3i_!qU}I+#|-UAi@>^+ zpc~pT_yZWhn$Z<2le2HnQuMz0Wd77hEKFQhN%SWHaQt+z0hB02;bght`RP%cU$ffB z>seK|j)CBF#wJNskUSTdi0QFzb&Iz*ydWTl4Xw>hT$PYw%ze?pmHI9S7?jQST^Ftl zBUtwkv`f3`qsK5YtrcR(Wm9_q-1xo=Qo~?aC$`2zJu;$W0X>3Oi!dJkkZ|!C4l^62 zV+d|rmt;QTq9Su3%0jFA~9`~SY>BIMNk6i^}^Q|i`rL(MKlmh=VKvf z@`(QABfrw?|J_rf^?MJ?(=Ul5MN3e58{p>*`;Skjf1LIF&*%InQOFzV{W)n>_@j46 zJWoh%FVZ0eH%3UxhwtY{3i`1Q5J;3fI~5b5S4JdeL;~&|535cTxb2U#CL(upL=Dl^ z)seivlEqypb9?pD_H+-$i-9R8SOv!iYUaeL(3?}|$>spx?Lb*%z!rhZu6>}Y2X0b* z*xbzK5bv7`$V|2Gk8L4@ZO}plWIw?t8~gI{>=dSJ%9G04N|{cRtJz2Lg_FRLrjenN zE~HcKHhTRiLE)+C&SKw_{-_4i=~<^qRghOHR5cK-(4wDzQ~ii6RIfCkGD(Iw$UYn%R%5!xu8iIUnG?#hyv8B{y*ErJo?akYLEK&BNC8^|`u{NYR#9=bO|x(U z0fIXW?(QDk-QC^YC1|k03GVLh?(PuWo#5^k$Uk|W_uc>g_F6k@AI^O?2Yq#SRdtmj z!B>Vb!=D6jtOlCok*d_pcf+Xosgu%!37EP}(}8;6?uLLPC2!I$8s8%i0I^hBWJbc+@M&%Sf=?zjPZ zPx+!)A%)xkN9QQH0u-}-W|Q;pA-h?_W=@QTPLAR4w`OMGEJ_H5eFzxE6IIV+$~$u1 zry~~cfs8wKj7sxmDT`gpfs2C;YiQ|KnzYkh%Pyxz{-7Vd!fndSt^_#B#u;ydxbwO8 z;A8A_gC{ObJVF^4C>KB`PY<9b&qLT&+=7mg(ab`XhuZBqTPCXBF8nTqT{F`Oov6r? z^u*q=J`U>ps+sW>)SnHc6gh!pA5=yae;u}{{)Vjmr;I>5$p6B`kfssLXA~>VGWf(R#17A zZlenm#>-xw;acFp$ryh&m1^pDO}{)zxUWlHpDY3) zcbU~0iLHtw3L4>kuw2Eoztb$!Gp0Ldmiac!q#)YxMv`vY8mrZ1c+IiecXK#=Ex7PL zKa7D@4H+ic5QmDkE9F72W`V1F|L`xT)?oD*n#B{RkuX)MBCax-)?r;VjS6>8L_L>_ zoOjxNP)4$oU;`Vv_g19h_z9TD8vkWbkql!FVnokkee)v<&DTXpl_JgrvrE`XqUanx$+wWd?HSu`NBc%Zeuk93|<%_t|vHe(t zV$d|ys`$&k|93uCs~aDqRg=IwF&qK&Am7OtQxmVIR;DG}9g#vTrEB)6D+l z7s~00+FIVYUQ&vnJ2ZwiB&JPP|HAgwKhYRyELoS3Ae4*yuOruA*Zcpp zCP|ThYb#j!Uj>j{vwM>fEHOuGxwK+L03`-PVa`gC6mfF5N$zl)*@|sjaVTFfVqO?1 z1Csx)8*zINaCpY9t)&fbXg0;kvIUBfeK@?d-}*?qBZ=bEk|=;8JxfdTWAp7Palp1h z!dX16eYRrAkpbupXAO64)LPToS#1w80Vh&*u~7#!EwfZWntx73A1lRi8|0j&G9d#V zfhuSBV_@FoB~ws@Tre|>>EU$ELu8vJu7?Xyg=?hmDrhxTd7<#==^hh|>lRz9 zXUzJ#JRXXghAGd#qp^nCNzoB>_`e$x9y!qfNc%<1&3#`Bywp=LeDtrET$LR*ikKp; zJTKqKIy&k|h8fEeJt>AL%RD^#HEfIiVn`oJACL4V4=pRcBZZf9t@XY*7E zYR70~)6VrOVkBAkSW$5SNQ1@z`#xEY=R}z&AEfa+?LTVP3%?5+ z``uGXn>ysznt*A7OEJ%t<-{kG+;1|^)1ZaL`D5xX8^NSpQxs@vStV(Yq`4!vRxedD zD>OBS)%Q`lnUPCz`4B&(YH`PYX`4(W{TsGr@e?ikU_pHlO1u{_!DU^UThFPUyX6U) zxPW;srsHjOAsOF;2KK)agn7$jt^-hGqW^1S`l|#%&eUDd)y2Zz$h@h&BuFDpub|1?zkOzyLcFqrTmScq!KUu}qtPHq;;l!q8Y43EgP;`HhX^=)%& zda8lNB1Qyy9a6pq_30bJ?YHrO_5 zv)$&GzZH9I*9~vs)9qIPHfc5&(YcBJ?wsqW0PBX6bGKm)WF%mZNWZg>Wt&sK3uKbO zChiD@dnKk=@>DvQ)>1-4=DF6|139O-c6aKU*(B43UM1aY(^|V&N_4ZO)S5HZRonUM z4N#y>XC>zjxTLCywr!K*v*!I%!`kLZhOgGgzc6X(a-m>fqqFO|!OU)@v2pPQ<94$? zYe6!4sac=eBd;tWeMk@*$~~oHdPP~L2vsHS^eidva-a^DSDGDn?vXR0K-t`7wQTsg zLGpb1%_m)}byOtvJm)K?lqR&sFI{;W9Nf6W{Fo4uTMw6JVdiY=jEZV+|J0v=laceo^hFGkSI!C;8MjKJ zU;BV9hrRi?B`Ul&*)%3g-sJGFTufnHO<13xCkO%r>d8BE2)M$(H#uAhM}8cV-TiPIZ%`>< zZ<ieIpI37n2lMo)Ul<#rQRX^e8!Y?%LXS9n3*O-19GO3fSq#%vXs>#C$nSR!fmYkbaBbK zx8JWECDdW=3%{HF##Ks=p7#lm&v#NXe6(b2!szkh`;KyShSx$gaUTD6o1O{=`s%BxDngwnB~5|WNk z>4EPpL>rI{AE@yrn|mlkz$$9I9ov<^=6C|}MFS<*AQsr;P}VXx6I)`={j%fng7c6b zWN_>I8^Qoz3qXCa<_M3Nv7bBpws$zph`&=?&*PXBOqj*x#uQXYm<4g2L6k66z|P^` zhz=6>iOh zx2I>OybPX1(qN_)MhZqrUp2fcj`$jIJcZ`m)|#IT&ue`{XAa6Qn#u{qVhBH1(!=}( z&wodl*402c8H)r`o9=BTV)sMP&lcIOY3ZHJuUFpJTVF5F@6REu9TKA^Cmm2)b32UV z5#BV{dAKcH4;S2pPL>)KUHda&-VVco*G^>l=1@c#Ipq^eq1RH4>1-t%eAIq{^VxKl zI6hH~P7V_6MA(H8HqYYN{;_n;R*vtYQcK-Ed;WXAr)qxLepfAiy&5#X zQn{<_a(!eOI3&S|Q97l5UEfF>`!G}}C5A?VZz7f7#rnB6ng|HG)N)NyBsFM|!?_dn7<^}d2GI8d$G{&i;k>p}>swf~H* z|1GiqAn$axrP?(Dv5(3Phy_MIg4c>LyX2xW9^PbYZpz8DwsV6p5K;o>mA< zJ6N=}j=)(#gzVG=@8JY5$J^WCtNaIO3*Qn6mpw@Q@cr1nBtP%#J*KSC{Zfsj4h;;Z z7P{)AG@g%lQMlGfjamL2W`H>4C0>W zO5~-9lBU;OYmAA)*t*$OMT%%VY8^`S8B@z#A&=^#sTAkN>Ksi`R%0Yy?KGC3xzeBx z_s(|r6c-`9;>sq+T+ryY4wd?eQmjLTOVwFUxj)n zT!n(7t4N9i@$)=saEzBw+%;g}mmyvInfU?bg}=OjI}Qf|{Ih#zR9&k3`_x+p=)(L7 z2Kd?saDnF_qtE;J48;TTmBT?;;AY26P`1Uk^`O}Cez8M@o)Ak<6e@kHux*3|qtg@_ zaE1nH1VJwkyI#Wpki#dj$UT5AwJB!&j_tniV1rus>Fjd!5Zwl&5?z-lMl6 z4l)e|2c^*?Y&Ddwr3S5mO)~VIU0s(DI%}Ejv+pdTZ#{_Q>JG~KuJ0F+Uhev-$*#Tfc>MvN zTj_w<_@pV5ZgfsTTUARd{@B=2e`Qmz?BQ$g6MTX+Z%CD~VxHDLWf~0WeIv1zJ}?C# z4tY8YAwAx_=sK1`yU-Y@!P8t!a6;8DwT~!rFHjMIz4T~mx17Wzob89u=(1ljQr?#{ zUb+j<);YlYR4@C>rcl#Uv4K&NWy7KOdyvV+Jzd@d<_0P7od)UX92sL_Iv2~cWEf>) zo@PIxy{#k{hZmd6QCt{JmwMAm!dO!n@~#hF*PK@PSCOnafaj|9B_BdBBQ^by8xEDj zTN?oROLD^mcp>)V23{M0@A3MRJsyc8*bv2P_z%}5Mz6oTCrX6ZzOLo&%=A=cftHv? z*!sJfHwC@lp)2cK0D#ryTbJb>nh@+;YzSKO7xr1)T;95*A-#V^M`=%x55ZtTHF1(x`Z7GOCPt$yD!Ib5d!{za^Z>gyOFYn$rhyEKz<_k!N-YUep82cq9U1_gtuUT+^k4QuPK zwMG6n1oJ5)C9uG4>)J^N zDs{GTDk?Zd_UCl8>f_Rd_3%>r>)nUCWjPj?G*@8_xWk9d1V~*SR8dW~VQ%Sj*L2IK^>M^R40VQmw)qyqp(^ITl+=Z{dY2%7UkU z(qxXnHwyb|(l}D-I0R(M;wn}61+z!FYS&J;(MWhRWS_6nQ@sE98ciNUj7kTa%G6?N zD%~VAzm3$Igx=pfaFnYHuX(Yb+dooflc_y5AwoF_Tl{icDfxgf(D!jz94|0*-?TmE zOAAND`=R&}=)`tjTG)>Pqns@6z=pVawAn+dy6DDLvwS>&6~fPW3tqFSvMA>q?_@gA zT~slitFw&}pXn|^1omA8oL}2AsHrq4s|xM1AhWgF7D>3}NXO`i|AW6uqH6dIjVVxh zo)l+dJJD(Ex`=JSQL7(%fc0+I1n`~BIC_MkRIA5qSxzE9F$!zoX2)mD4 z^v2yCu`82|7|xycPKm8q2${IP^*w;!9nmLw_qU+Cbsymyl;zzp+JL6Z-WQ98WAzVs zce$lR?ZWT~&+;qpd?KSoxq0_+Ux6U!PIvO|Z|5LV1N_+BuaAk;U$L5~4)cug>8D0weDc;AU4mw#cQ09-{a(EWqKl~#E(;ZDH33gvvfVoE%VTdclR~yRZ0aznTm$QBuM@vh`RC7## z*>6jKz)<_ZJ^~#@f?-d&>zXx1|K!6<26@a2uVG3MwvRf;r!BfKbIF+(Hev19m_NSI zDq4gA#86iU9hq*V=+=g6-hRh-8m$Slha@ns<8hZ7Vn2L`-yZ&qCerH=)#d$pnjk9v z*Ee28E|NPjWpZu_eHIsHE-xhz2@xYKEvDv3&lH4nBR5!+9z&wQpUK2cl*kv%z~&3% z$KpEn)lJ#fKIkd+PE6AO`!A5V%3+SaTOZ!r%J|Xi47Fv2=#j=dn<0T)k~$;EYv3@S zg|d)c6@jxkYmAWUIC;C7$F|bs-OW|D4r~2_5Qlpy&dx*;nlFO;&g_uBz16Y3?k-hW z%De`T{GxY&DdbZp*}e;w5@@08fNc?kZ=lF6n!Ppuyb?&<;*hLaik~MH>Tdd#8aTlQC)8h?BjMkgkTPMq@=+Xv1V4#{atnEvUFko#8&mE`gc=i#33+1-9&&X} zUy}(pD6G>YQXhN-XTdsvsVo|=Bxlf$s+!DkUt^0{qwjaBT_`%Q9^GIiHSk={90weHOCzV^hO6r0PL#-T0WKTUa2r%G!NoZE`L!CKpPv)BL)W z5XOX(NClK5?09~O)nHqEe1*4KG;7*M(_rPop>LyFDYkO*Wl^%94<+E@i8q9&8o1*n z*cPNG6(zsV%27n+Axa}y+7O;CTDf~jtaMGRM8jMVY(C0w^$7mt%G3CpcC!IJy-7xI zxwdT*7Jl4yGOZ%!7!U8!xi(Eo-fI0Edg;eZ6_fkOLNK%|x(W$mM{3(We;h3h-R><+ z2OIp`dydG)2MT@&GQ=YJl5MtC)}Vk|)FAH+s?X1i0Woqx2qyifcKXW7b^5Gr)T6a- zKBm!NjupV${LZa1UL%R3@GVWmij282Jh!jyY5F&@Ws2M@yZyc9xFls#pS$o*rVK8?}Y$41Rnuw)ctOV$7v zDh@m5I{a027#Y7iLt+GJpcr8}g42YaKCSj}v{>f^*u!glVN!sQ)mis*S+uj!dN{bB zw7wH&dcV%cN}bq7de-s|xaqAW~i5})JFfPYQq%@0< zYt3LD_A`%B!}&s!)s+*UMXH-4jUv6%V~Lu4eE0_U2Ae|^Rv#L;Bf2^%5JD{_@p|i& zo%7Z=?Hl_bE&NJ)UOH4zo;T3|LwoWm4%4z(j3%Eg?iufli3*M(mbF7zhnP49k^wb^ z@hMC#YEin6uzs^&hI}nBu5~srO|pbnNJ_+W8ejSm%zF5Ba~Necjg+!MT_QbpwY-2z z$t#MoOvx*=;j@yGy7#zK;%{Vy6r4-DU_WpbVJ<9E&3(FMtz{oa2i~GWAs#`C zH&RR|;4+A7Ziw59Vmw_7ES8 zi#y?A_73}7XT+noxVt6HfQBi{=}K{EnBoI^VRZ?X(^UY#${r795NFh8n?|~8GZ}ic zZvg72@$zX1<-0Xy)C~OzBH6!YuGF;+uUw$CjSujDJ1G4t6Y4B%Z);>}_g|fVp_;BX zjwl+RFnc=@Epfy2hr)+7BnxFJD;k9aU|G51{0FV_A+i-JdJj?3qi(f79>1b!$p%x{2VrWdD_&72NE(_jq=P?yP@ z3|LKaAF`_N=qa!jx|0j&LhpBHdfChg5aeeJH+9B&_U>IZ8K4!*2Sv&5xg}n%CIb6M zEO3)-E<7U5oEkkjCo{v9tJa__^(5^N-7<-*7u2!f4>z0^4x+eZ)I9NZxZ8{g+Ll+B zZ{0|TPPlMIak7-5Rx{(d{d1iZ*PEXO=<660=vjNATCX#*Z`P}v#gI!P@7S78Zd_&J zZ`;2lRVF&v5UbOyChE3dta_oFYr`$?z0ynQAOtF1d#)bY1V|aQF!%#`+4|Yq@5**@mWz91WsVvEQJa}H+MbMfN96vfF)N}J_GV;n& zjdQJ^pOvFwGLOG~P9CIlJs(jPOpIgX#dn zR}CtsV7=k9sp=BE5M@hpK+L822ca$;!I2NSvMX=ErP+ppvt(qO{XkfzUDqj>MySEt zM8!|XtLTrIpxD~G$Qc*r$7Nrm;teg&@?Jz7o*$>tK7@j<@+Z5V!C8XM>wa1;2(1wu z%Y^AO8l%TI-Rlqbs%u3=tv$k1nZ-K9@<42&r#!9!i>B{TKQosM_SD8BG6l-)BFxhUa zqtTs2S;x%f`re*`UbP$6UyKtys0-aBd8YNkK~)ZE0>Pbz5$CP-!l`&ciTS#7YbT5B zyCj1eI#!mXR_Bk@X(`*9_0jV6OL&$bcS&O83y;k_*e3%(e{!(@YNzC0P&eBIkWwtI zgytaLi7)s$HTo;q?Q!%o+yNzzi|oAK7$+5l1Eg)hHIG>`A(jd2J$Egp0)P@J)hd>L z_k(2MxC1quZ!jxiHB8{VP&`m6LN>ghicL~l)=JhREs|YQUiL(mSxV<~L?Mp{(1ZNQZ z%OfIZ%~c8{f+d!1J0Yu6lhW9pnb1=1XxA7G?n1{RFLSL?jodpf#1w_`pN~3VZOIa%7qh3R7Z>}EEhq|EP^VjAZFx&< zDKahGUDu+1;cqAQ-hUgSO>5eCF=QPoyp2*|?BrPPbYOh@Vz8pXn9s3n_9ev;x90WE zUq1t?>pbr0Iar;QHf=IFjUhkA5xJECo~Ye02`q)|1*thZR&*EH=#I%ZTVV>yJE)xE z!il`G<|$lkz-yN#HYu<0gF$$*sN_a6UT*0lA*zAdx93tB1t4EOoq=nfpPc@$Ls7uF z-Ogf!z`sF@pB%<&0zmKD*)nz)MEzY^Je zEp14`NK}BaoMo3%C?{NooVSpgDG=S0UN#$U$*^Uo;4a;^zX{AT{*KG%@as(dcR+Xl zUS_JSP`oS^!NcZCYU|_Ii_Rb9e!m~jIKP3Np-ts@fE7Fb5qfwNPZkfYj`9C4y26s0 z^`@W1oJ}ZPk=f3=)0SM9-yZ+eWh8+yewrxN=@R0f2q2H}r9pC&K3mpHW=YwVOkY=d zj_tHR#TBk#cmAz^DygLIH2^$o%vqFag{zWonwXVKP|o*mRtKX2l37%Rm5ZJP14 zuV60E+TneFU|xK;>&K|tB5rXqOYj+(Vk%i89n{c$B-OlJBB&054_)e?Rg$F-2W8=r zi6@Fx%1*1Pie`<|iaxL$>83;*ROCF~tC&1Y;(Jhg?2whpp(WDmrFG=k$xFk9BPn3wsLS`Hswsv2^Z+-1^;HwzBztz^AxqvB)T=SyV&iM=WzL}}gr9xRj<4VP!hdPfF)CSg|C=vzXq zWBd+_F=e>g7s*;}yx8}3f=r>YCJGN0@v1S{UZMVquB-lv*Vmxs?gpq`vPZOW7LEEW zWO$yohxX(O+*g180-zVkKm80`dg)v_Y0lo6-0?jsQbcSDKAoZ9dUhU0@6cL|G8~C| z1Iurij@VrF8Nd;mbanagaurHT(NBxhb;&egYPDCb)!+nalbq*@_B;v9Qlfbqc+KrLa6p7td*&izM>S~6iD9`AJLa4o#EUy z31p4(tnl~x7DGg(RTOeuW6+m%eg&KVhzLz8<|_wj;tZ8CLQl{$QiaC(&M%RI#nI+;irhY#~;>?$)4=jwedhZ#qH!dF~ z#)Pauff^;QqQ+v(o@w_vaHOR2P!v@wPNPXO=v?!`x8#`u`%axosxW1k}>>D8SESK z=|fOYF;jMwsVwUSha~GTAMs;U3c4`r6UfLQ$~$8x>G%gnyA33dXE%QmU0}K@5SYXY z*p(yD?iyDp-(c>rztjOF9_FtyJmUs`|Iu3FYcF9=f*_8?zd{`U7kj&ZAr57bn#JY6 z*v9{FrJ>scCR(M{Bh4Tos?f6wVy^$3dTl4tZpdoZHORV$AKBmh;jN+atNC8f2!~yQi`1A%n!V`+^0B>fAH&ff7A%T1_l%&*%_>8W=WWL`~LpCN2Qiwx}dpe!sBd2 z9_otq!?eTT^TExE(2l@{i2=2+UzJi*tJ=Y9nkokX?7pnZQOfR+rco&kCrPo)lESjm zs>uCzMc7L|!yNGIqO+$>lb!RU^z{JW`2jW8Il?}T=;Z2>Rgs$oR{NBY`LB@%EThaA z-Q_;T!Xaoc_RVx%*OH%s(v?0?c5umXS@o=IYOc8 zxtH^J=`yV8c~VUbEPgs8XU*_w0j6CX?9?yZz-y0p4eo_mGqO(Q!v!t|kd!j>bIUh6 zC*aJKd&yvBeV*bzc`NxIS$~dQ!#2a$C#w~j&Q;jv67;$(9CIB;ac&GnZqY}SeiOt` zo@yOJY-X(kj6%j321#o4g&P}@0fv#3En%SnL~tV>g@h6C5S%vD%YD|sQoT8~nkbGS z-N((*^|7+93Y;JABb8*QZK5YAi}lCzlF7|zGVreUc~PI-a5KC8L@=Y4F#OSWhgKS`?12jy-EPY z`5^@^zxzIK7b9v)w{AHm8>LK*IBMnJSx19Nwd7x54`C>tI}OwqRIqv~6h=dh+Q3ui%ce-$BfM-);?>1w!)ye;Z4HBS zIa@7ZHzF6;&j(9OF-^muTK0(`i3d!@_2~l9d=9T`KDNk$yJUotE8k+qbdwx;h=%4O zxQV9cD1zxC;fci#k$}Bo`XYZ{i-c`{aj?XF|K|cgE;^~}54M5wS8U_23bB96>CZ9C z|KO{mR8F1NRFK}$M&oSIv}ACdETyDdo|LR4#s(YEl!_tz=MuP}-6L(vqkC$jT$Nzt zdjqe)%})FcfqVi4Aa&T$vK(IP9>L~@b3;4G?&?T_7_p+k(WnyW&{cAb?nHS3Ee$jJpp{R=mt9oGT> zCB6r?2?$yEgHGSa5z~CfBWU&t5`MT&49)@?evd~G;C0#%t~!IrRauUGm-U$qAS)je zg8#y=c+{AK8hYw7ae#27-MW@AvjJSV_3J}x((*A|sV z%d>DJ+Ik*L81N&a_c=8LnhW*XBP3g4Yss;hvFfUQbTKM44vog6cgU&IB#%t5At~u_fO_F z4u!}%&;&*iT^k>|xvs78YlL(bkZS-e1sZMm$s$4BK4)=%sqRG{qY@s`P55)5~ZFSLAS z*I^Gox*vT(0beDlE+l4a9B0A)l(7T`j1Mrqf0fDJ+5z0qmDem zf4ntp_Sr_NLFiy~Nq6B_ew*?+O9E?Npj|LovS+J?w!p)JIf7S@3&!x=ia&Mj-XF(t zp{+QZX_jiw?O1!Yt2uP@c@*3}ugS5$iIu;=EIYTWC++O9cH0PyTf3I#Uf4i@s^qnD z-&acjo1sI)HU9!X?3f~1?tGy3MtN`3ncc3Z{uTS4Qn9m&d6JD+6MCJ|#Cj|#&2Sn$ zJCbqMQ!tnvU%WFl=STD26{=wX=j@Y4 zJZGe_jGSWS0e-y{_asN-=;Is1oWnWmCV3gAGV!cJQD(!>jH2N!_$jT#M!d7G_M0dU z1*7dAX#|g%ZH5u+uo3~Kg+ z)L&isj2$0j^%`|90uq4vvOrA1sbMyLqh3w)6|h?&XzRMmNK$F%^906^*%f0q3LCJQ zW{Y`g2VsYs(ECLHLbG9)0;q{;Alw>-y@wSYF^uPNb_7^XpB5`aY98@0_i@R~T4HC+ z7VqBbR2(K|5$UI~j?TGgk<&@w4Du8CQV19mT8bxs30#`r0Gk>@;72VLec|Co!}JVYGSHD@D8+*0CEJnWTahsYj8ZXpyS4gytD|;SxNB%#6k* zKSG)fOF6JYJ9uo(vZ4&gCOwWM$SND&PG;kbG5X@T z)cny+NeY%>qV_l`vncZRG&!><;RpK7eETym{s+2S_Nw<_XuSlD(*o!9%_p0oRU=*r z<%_ulNBE`Vwbzqg*!7$(s3%79b&5(Vq3|0N{tXH13R~ZvnU&B+tW#sx%qTD(@s`{+ z(CshxW`6wfAzYj-nc|4(zyI;ijrxpp(Fb}OlKwikQ~Z@gQLzUl>)4t9JGuw| z&!8g=OnNDf%5*|q7e!>L^-p!y=W2H601_tUxKinE2?g~4SpJ@^EB*m%oKN~AJ)A7& zQ?2GH$<4*)A9PvAC=JFvZPz!JOOk7J`w)8F-g+H8PVi7iNK|z3ih;%~2NqSI2m2Wj zCXsus+=qfX%8TB6L4W}6>E4!!qH!QTCsbe9>$?iYt7tL^~w1n8*eWKFsS-lOZU z^7zxgnp0{XBg{bp8Jx6{af**Yx@d|hP5TM)rlWc<5bC0q(D4q2zme~1KC679kwyju z8#TDvnE0Dv)L6ByfsEScy24fw+g*Q${F|cV)HuJ*W>&#d#)&;C}EV3fj zoAr-fU`R)mQF(-s-tfI*_kXiXAS-V3qsH5?w;s0EL9>~2HO*=$-2)U#37Srv4;XI5 zu!4)fi;^&rINdtP zE{f?em;ugpg<-UZ(KnGWCRGNNfw|O|yZRSbIQIy5ziNVH>+=Rb`_l64)9?zi)`#m4 zFgkt&msR-^RC~e(ME2RFsGsztJ2hWoEnuuL{0t4B9IH~ByMvWwJwjBS`%M;J+oLze z&XvKz%rsX0X)JD(@s%IeJmpL=iQC~#3b^wi+q~9ql5*0O$q#nYNlKfp*L<{AMAo8M ztXSZh{~H0Svu(p@)6qs#>q5UwN#!0e?8zY873HYuBDGXq6=^vlkFLOo01wTmlUIz? zcb8wPjGGilK~T)11-XLknMU+u1knU!DO2=q6QV9Hdw{aavlVR!OdwhU{YDriCiROT zCCgW^Lh#NV0w@KnD6h8>N?E)h%B&s}0S>$)n$oLbde_2A-%8z0%STwDjc;=8$}RXG zE@JgUy$7rt=k`moZQZeQyHN3;S?wN>lwTqy4VscuoN6^@NxFkMx#E`>KBOmrd}R(I z;=a4gk*={yt^?&jp15vU%y;@dncr%VXsJ_)FjOzXyc>qgrcgKU?UD=HO~STm?}?M% zHMP!^vkU)~9AfzEH*A5nBay$}&;0e#`cpP?hPMB)zl~DSRR3dtOE0Ugq{t4jPED^n zO|9zK3vq%`nSG`Kz@_D8@B~i7MewvzHbh6y~)RTg| z(q7V>yQvpJOIP_U+`H-#}b3B8jE-h zKn5CZ>kW8SYu>WWtB}GK=K#9OsTgnLCfu$9``LYm{miaPOu7v_wDJzq)u^?Eky-9b zXk-&<7Iynp^6qh@(St=mf>S*`Dl3PJwXA*WOPguMXF>a4mB)-S>S>i?u{cRH7RTwL zKI61iDr(IqTpVgtl>&qf+M2cuVS($PF|M_W%T#RDHAl4+gz7NnW2^ujGV;-(Ib0>O zl5JY&EJ^p7eM!>|fp`uav}<2LMmRqsFKV44ilo|yq7Ze6zXwCkRIq~%B}#pmr>Cxq)@ac8zLf2L2CBdriwu?^83V$E zm9No$sHj}&45B2dcJxRC^Utf!(-se<3Rmu1QG^e=%SRk|2FwjN95(=Bzv`0SrkFC$ z=msm3kE)&K@~R21yG)&g>q{?2r^-U$Y3bh7sO`?-f@AiN3GgV&=>?;M5oOQXs58KV zbO?TQwv0^6Q_B7>p(Jq#iL3`yn!tET0{AmSkXSmM3~p3vac6{Y+FpzI7lka7>6C-qz43R zLPw>*4)!i8fA9cr<2d^vOoV%g^*@@#Zg?ba-4On&z#squlfdQhg zfx0kI5%`}fLdWmOjo292NzBxq!dfgSj)O|(=M;0j=FEOIVCnSIb|(HzV_D?}TDM$$ zRFM=X{a`8?9!!>r!hLErIM?ox?FQcV;lSh`YrRKSss-mHX{*IR7s_jGQB(b2XZ{f( z$|42OWTE@l!Yl%+|4WcO8j1r^e>?agT z{m~@5u{6`>V>#q|hClXa-$9udehJr87j@R-d6rzx+|C!AkDDwY!PBt(hjEY0AvW5g zn4vh0q{8*bn-EsDD-Bxtfja7>V#$h85Ar>b-IEf{NaPwO)L9X`ac%gkMidP%l(VMb1+(zfKa(X+kOu~RO*T_`eC{B~j7Q%H!fgOb+q zNIl8!xWmXQnnbP?>4aQV+?62NsqafxHB6kpa`h-_uN!Y1NRw68TYyR^b~A%+8oD+u zv)9>W$+5*0`TC__&AqK-V?Su25fwAtVLjy~Vn~5y&0O3w9p@)7(iPZSv^m_3d!t^- z*yvuhUQioXsKdWkbt7SdGI=`!&Ls05=nvUzTWt=(2}cDG5$lCFG3`*O0h!w9GHF*ySPo`B=yHdl%{7p**(}0tymY*ysQAqK8x>W2T&(cPPS+OEvrn)!n zE#mIP?vp6RScobJr-&j|^kw?6a%<1;JCg)0QpR2WTY`ltGZIu8!K&je+-NN2UhzL+^7O!Aaq%$v zTzofL_9~Q!G7i4$Z7(n1aGtu*J>Bp2`wgBGB%c2zu#rbDpLn8;@iFLGMJ=><+=tnp{KW{$#jTDOXqPa>Gg%#t6fXU3V}$_owLddpw{Gzk{b* zgrMz8Mi$G)FyA9qWZ9NAX6qj3dDdHlh5N>?o2x5FiQbg z94g_feLTxM#pHPDSUc42I9!$|vtW;5>Yu;DO3T!n-vWQ+U#%`W7`yzguY>>5KiP=9_7p*yQ{3Qe$ zFTPJ^rz6^O!eV*0HYzO|k7xdxt!qYcxS6Y~{}E1I&W(i2S0K=^&CkON({78pGQrKB z9gF_$cNNP-agA#~n=c9ejF>kp946#2%2ae(mE%%A@5tM{bE~nd1+X5N==;3KY~S8i z`#NCh^fgu-^uK9~^CLwmt-CIv`#e3%#1y4Z((t=jcKJ&q3Pe=y@0)2lVYLpCc+t{} z@}j|f0)I?Xa0Oyz41PMIKeGI!Sz=U3sWzx4DIY<`dyes}oFJVp?v|B>F~Z_+{0{K| z1p#Ltx6m)F)1!T3gMpbJATAO|#7_e=(drYKa{H<Loxin>J*J1D&3 zj8u^iJ!@#Jp0UvBg&cuQd3Ll4oBPBA=wkmCcaRT zh<~rvsv?4udO(fT;IAt>)!$b1KaEt`#n8p|fBD7f%436e#AH#- zqdbVgGL7;)j9#fkUyE9T&_z3;UGUvtPEKk{eQoHa*PjT$52eh&-};!XkT^A+=U7ZDHo2i-&% zgS<%z&q~Uz$`CV{JX!YCA>5)ZSQ*nZ^P*I+HL71hrfjZZ-RjJ_RV2>Z;UrkUPO}z0 zd8^&oVY^$-jFp%Xe>NSry$esF zh0GsA1S_F>buU_y`>&t}gB4y)UwSF=n@h*TZ7;hTrkKl?EFx@^#Lyq-)VfelY+Zca zRZuCulDpxGXw-x<;p?``!GdUUn-Y5Q&iZ+=u*`eZ+N(+fr0k1Ui7!Q@F%}2(l9#hU zG?tr;snfWKe8TFt7+p4V=Zp0A!J1TtoWpjLu6pF%ZWpE4ABr^WlGL)GYx13!LAG5S6Q3NrUq#~7(h!xX=jf|^;SI`wVIQeyO01Z86=AqXf8L4#V_P>vXA_rmE> ztqf9!WmZ{9f!c71Q8Z|I=u%YIl424qXK;j7@{yoBbO|^P6>g0W(2_!^8)Oy@t-lj*v(pL`MQW*l4BQ@LsZ8s1+Jji$sf$LCaYe^ zZuGS_u?y9?31tb8YK4mm(fEL=+q}RivAYiO*_A@s&&?74WT13NalysxKUN*37e9{AgFo={p`2C7$;Iuo&o3=-ns^CO~ z0`Oy77xP z@es@@Rq3qy+C|sPnH^Be6B(Zw+OSi*lR+&_TMtGe_q(u|yQ-%}9{E6t|3J+Y-C5~OEFp4HYRl+bdS zoZ-Knw0em#WXa;5lWQ!|lf7aUsV4kYG}UCxy=1-0*YEIz(f<3juu5zh;(if# zI`Ra=I@QEZwOLF&7qwnWR3NlayP2w;H;rY`=`587#;nJ@PPjG4(2cEKM0&^$`(@}0 zoHDOsii1?!hQur1^xAN$;5C`Hb$JPU%Z!rRG^uW|ehaeGQBe5zip>LWlVM9( zfIS<~Dg>|W?3l%6v@iIZ6r3!t=HluAZ_J8!FvvMc3FLz8d4NnrY<@qgbWZ=KobLe+ zyK*VJ*?Hm?$DeB!_$J#rO4iMN@v_l-WmnH{5!!O-7hJ_$Nc%)s>40<;IW1bXoVKRM zd2!LK;w35B4g0Y|c1q&xe=7M1GQT{sr!F-YOJftvKM0dMy76FUC$UB2x0^6`b{PWM zWFQn_ls0AL=6-4{^$Bv66~L_`uo@{5qkytn?y(2lrGMjYsa^t#ML6U67KTW28`JX< z>K_+s9SiM}cGTeb{FieW#N#drBH*6s|KmOTZ~PFz^CkSZB=)~xt(vVmwkm>O+RqC+ zjqgeVN-08=7-(xNMonv4vX<7!#)4m;)v(h|Afo{c*|51K_jh=|2k;Y4FP33EevjW9 z_=k>1R}I(($a~=v6P?`5(?2%@$v;0H*aJX+C-nSymKcQKGjmEGC6~Ekq-1(YiR?Au z{dMrsfxJ{WyN&Hc7?G{0)NyJqf4yIYL)8JYpKyRhT2ez^|qZ_X3J(JB!f|!WVzk~ z>z(}jh-AQx?VNS~P)=8fO5a{(E>4q`8rGQG_Cn({)yHg8j}(18_BBkEHC;0qVGkvK zzL7NN)JPeD>$~sbH_a(l$i#(uV)w!D$tP1?2C8k7WbVdg38Hw29Gg zp>irR=7~8gs|^*zlc8ejEx<8_3pOhZ7LP#Hy9a9qvjaO(V=lW+Bp>)>+twyY7G zcKon2Ei&44C`7R<0lqhES!0YDLSy50-=c6Mu4s>|mFalli;sgYkG%2>;e<(hg4DNv z=pw!=s$KS&v}0?m^C%!%3{@=&-+6WZ77&`V4pfz=XU)wed4i)`>5`4rmW*M^pX2Rm1xRP&QP$|Ft)7ml?6k*-M!si(uMfR@|D zC5L795ET9@32W>j*MBE7l$kRW5+<28WgykBDKlQj%3)!(GUyBI?NP8xh-di>tZ(%U zrEmQVwr%wcw=JDB zpQhSAGv`T&s^slzn9hMo(ll}2;#nbzcSuT5=m#!~@aJ58WwtDNKn{FezSPHOcl=b_ zJV*SDKUhEziP)*QYpF6mQpB(4WSm2Eg6G!ZQ(OqX)7(>52s!wmXM~pmauF8Ma>_lV z+l&wLl5L3sD8$7L>5~j(cjk;I7(bzqp1^3Eg7~JH^63_Y+36;P#pGEuPprr$gym!> z=!QHMErt7%wID2yZs=5Ak1b6uawX$a9?(tl5*A5B!cGY#6$x~Tri=u-xm=nOP$?nA zIq8%FDL=YhQ~EVY`pyx9y<_1~(ccGpy@$k$&tDVuxz+Yne;#4xlxuLVwepFJLNMVt z+P}l>w+h`h+*K7C%uX59h6js!19hN5p`F7#ig9QFyq4J$Z+l;T>YnLwduxnm!J{M8 zh9BlxxHfyqKvrUBCJv@DH({n_u1J4qdr*VOoWi(auQD){a%Y_LgtrLyEKC8&bjV<3 zZs6HyQ`ZKOUG>$Xum;sIfe(|sV*LC-dsy=2!^U{=$oM->-sYjt$~Yu<{dJi5Y{8~K zrMI`f+7m;9zE&G7LhmH9f7Q(WnX}U=^xIO8mM-3lXXrQP7Q>b1((w_#@eO{?f$aXM zF!-;02i&(n6Uqcu#X*{_VQ>@yZwdU@VpN;>-TuSvZM^5dwgs;+G$2<2yxabN#E!&ZV0Y5luScnJFNH9Y5kiH6U zus)<*hzEJE4S~K$BhG*{h)0-3UxyB0w5AHCW*~8J&YEq_+EuJ68%+4PWwYWVT$Pkd z98{zDqdPoOmFj;-I^6{+6w|}`BEo6nYQq#w9OdzE4KOv z>voJteAm>1X_cBsiS;Ns|RngavEW3sTzjbYcRndj0=}*yLt7~vTNQT zE})(5vx@#pRr(d4m6y=_XqVA(;n?9iMDWjKj3SRY1YwEJR5Pr%?aa)mUolkb!}L(q z6I5o&Mfj5tzgKxgs#8IERPVt?QEeFoFU{Xynd#(b=Vi_xvxd#y(Z?9BkQ;a|qcOtx zR=crjBO60RRAF6S^joI2+B?q|?Z^39Wo%F^RGjW66F;u22@Tnz<_=+!`OsB(yM@r` z2I<^wXZ$olJRme_7s(qGjrveRx{pU5K%gr`he zl4PDMynwH>H$jkRkHD)usq?|^iO9Z*iwc*3j}EyQVFX?sHKtgtGzR%0+-n^2lL|gp zq9v-XMmnmmUC;!04vSPeaY?oz=m5XC8K*HtT3#}&+a*C+>%eZ5Zmr`5?$CPK&Tc<0 z+aaF=0&VvKgmsq@-$hS*qQ6yoL*&-G%b@qlz|y~NN3@5j%b@VeK-0f&Mzn{FL6R>~ z+re2G1TEt^G6IAAwBlr8k+6_^yiChfIfDoahSSp%X8u0A_bYmV2tcnxg_O)L!bK-1 zu{gMfZ_s-}(&B4I>Sp=L6rN)5yn=IeuP6G#JZEzWDhe3iEC=M+#r?8Yeurzo8{h6e~gC}e1jZy}AD9N}dpnnPlj4~4LQ zmNkaxx!nEXu*RCfe*k?b4$Y$x608efbMo?cUHctxZ2Qys1FeYjM#GG8vG6Flr|gf@ z;O$Gg@J#kBD9kFDj<9HhUp{8Orv4g}@A|F`Q}7J#2hLz|-)dEJ>cTxQK@b7yh(R$h8z#ILmav zXz*2Mf$%#q6C-EzSR}Q>(W52OxjicDa3xR@F*?*DjdBLR+h7QnINo zYg`%7nEB$Tx}QX2$Y|o|E^PVkao*t{2JaP@XB@}N=i&2+|7&u8V$+nphim}c%@|Ib=0tIu}0;rDXJ5Lh()`sa*6avDCQi$Cq7x{>NQspWdR0J!v$q zXjwE=G*z^)7+OqL&~+vmOm3u^gkMNtY2U;4&tTn!YuRBTPig1fHo#x$ zR)Ys|ef6i89>i$1oos(7c1V7>zB}k!a)oUonC0tLV#GP?yEjwX2?{ycuRGB~W)_() zH`FMyTQzrOFFs9NXfd{&eosFCW^0@2YMd&sbbxZW%Z3Yy-tn9=fXU}OZj*6k3wb+n z5f(C#q>4(ZjoR5&xL($!$-bm9lh_c))~Ew7A*`43hB?>0856rzXe0B0B0L`sGXp>TEH zFf7weXM0My9rk5sX(ofLFVcRb=IumaxtVT4>C&4$Yjim$O5;nDVtzr4Xwji*^H8%=#kcV!ki&_1;=__rZ+ks?j8^}8E-?RuG73w9Ik z3O#{~^l2QApfFQxNjdN%8S$K=$Ei`HJYxM*>%ZCoy0BPi*n=gyxuZH=g9p2ZHEPEGj6YXPnfW96^@+P z|J%x<@jPS!!WSGR%8$>{lL=hq`(R!f{%a<&TOu)mWLbU2lEj@Ma_-(v`g>3Pc@PL$ zVFH#(pi{wLrGs~+l5>=aIL{TL8w7cg&TESg#9l}U*@6Z1OlS%Xo%o~^#iFEnTYrG8fs zk8E3zJ$rjW%4;A^Qtow-KxzHC5N1y1X40+LgECieM-S7yG~xl2xxHJ1?-YNKFgi~> z@MV3i9P6%fGzeO@cNpK5)ZF_9Lzmnum#EeuR07`4fd+oKHp~v%EnZaPQ2~R;c z>i5^4IDcHu*dm@`V%b+1`KEH@F1ZH3yl`*34@|vh=wP0`F5gFpZAp5A1c5lDX8{#j z@pysAfOTa}=vOwJ9p~Y3Uyh!?0q?irt?5cY+B<{!&(y~pPa*+$Y5(U#2>8q1$(-H= zU|VeNWN1ro=wL~&BJ-!UE#_)t^S>JdHU-$gC<*CG1u1MG7FzX0N>u{UV#EaYR9yq4 zntgpqgBP&$)mjhe&x8Jj$0wXeEBoN_`R|Y)81c^6c1R7YvbSH|$)2Vrn5Q?|`aj;U z@%@1_dY^1h29K*x1jFd{Z$uBr>GT_XMsG!-bKHJE%VR$#9FOzqyL91~JMPVAT6SqiBMdeU~UgJuXm&if;9Qave z7R!PVUt4d~LI2&pm}Qd@vR?tZ%cFu20bjkWO!`F54a<*j_0?#uuFErQFGA)v`Yk>f zw?ee(Fu`8{0tD1Y(b6l82I=%ua>Oq~xN@8+_1*+2_4b@umEFA>L%yPkGEEnknCg?o znXj+Shpf#e6=|tskyy&`V~tIu1hvEDS?qZ>sxsl(YN|IJ_1anC^3Y`LoqgzKPhxH!RPj zbZRv@bctJ9*{+7v7~8vMxF*ed+i;sH--n4W>ab|M_qHxEor9LNsLa~jix$4NTb;-^ ziZZ*?6>dkCW{)l+anmtIl~;-r>8&6E6c6mNiyoh8s&JWe$x30wJUAy&{man!RV&(- z&{Hv0l(AZ5xQ&6^hUX(+>8-IPF>H_m^Kh9j+bLROTZ4dLs9M7aL6;f78-Nr;*%`?6 z%EnN)hB{)Bf|7HnI)pic$^#s*)t|Nc0zQ=ih6%IC1w7gJ)gjNO-qOA79%Vb)KH|1o zPGl!57Z;r!&QH$Y*kw#-q)x2AFP>cmrj>H(Rik+|d8M=1ZooalDv)W*u%Bp($VYR{ zTN#?|;XN7RPrb3PppuUjSt_M{y~XvPhbr)`Mk3?g3g~A4luxTN&Hnh5J(Z8w3&hF%q;_?U&~RhikY*gpjn7ELtIM=@B3yZt)c$ zp^r?ZSV(Iyvk=gisBCjJLuYQ| zTxo^qI)X&I1%Kbq z$EPWwclcv44c#MtC9o>8gd!1Fu@i-_w>+h1FomxQt&5Gdebcm72KM(~RDTj$o*(ak zT$%v)pShIff92BuHqQX$QUxb_GfNv&0PW^KI0W*@f+)Q6Xj)@xC{m?GLW1=ubyP(L z??J#?I-p7fx0RY}O*NYBCEPp1_H!bC86FhhZVu=h|77lXFKE=l=VJDL+5$M>IJfZo z`M*FJV4VwwIFv&}DH*jiv0+Qs zG%>&oQ&^)~)uFzno!pqm{M|uMYi9~|?-oi(k>9|bWyz$YRPDq#>Z(ki)|6Y)Y)!(x z@M7z|%HX;NQKUbrpAwm8;DX#c@n> z7S&3Ugoc^A=)VTlL41b0jO{9tu8QIZhu7~|Oz`P{BH6e6xPmIRDO|7dCsJwuI`2_V zuMye;Gu!$qwZ5Dbma4P4-{H+OKHlC;?8s&7X%@jUT{x7!b?~4oX!CX(QCMEUz9YYU z!(6I$daKqdnLDH4^bVUo@h%%}p>!vQ)W4&TaAG&gs>dd^;E?e<&7e$;iY(VcN)W^x zHAk>Eh%W^7p7>(e7!>n6g$J-|uQHEvT@-6rqW`)a-xVv&g@vJ+Sq(#WzA1*SahD}S z)_ji4MU~eNt)~24qhkCx%Y{oacA3|ScD;01xu{Ue;}c~o&r8D0*1{UQ?zuZ1yvB_M!!U+(Ny;xo)+B%I5n9TKP`Trd+_uV;$^u3psF_aXvD7YKQyGlVUX z1$PL2SOP(xlmp<1c~GVPgHxi@o3VL>TptwNGCrK=jH==F_1iAN*p_c*B4cg`-a0Sf zH$E1WpGb5apz4rf4(?K?teuF!MHZ^|e_NFheN6iq06Gs=|45dj`|l9wKd8l*73|@m|09Mt4cvOP2~`4)l0;d){qL7;o_%)qat4 zNDzhUSW-3ouq#gK)dPD2S%1*lypnwj60jB8S~Ro~v3MoQkQ92A2_4Hp{%%z*FVXHb zVl_M8LUa->5h+OEt>0sA_T5Nmb3XAJ15<(@30Yhwb^fl#7}J%AS1Ir!rfD`kxzf`D zsiBL-j$p5qSBIw;)~2|FOsP47J|d`{U&Qbb%t31wa`9g#4#}B<24@C9Y)Z=xXZx?^R~C zbv0~Ll#ehRGcp)8N(1FTqbjg9i^}kSjjDRVZd780nL>IKW+nh@e?Pi{TlfNMO06?Z z)6wbZe>%MY^M%^)I9>aPWqwIUpl^8k_+0h&f4jGTzT4LK|H=?=kK}=LU5U~ACUeE` z5-=Jr6D7lEWW=Bu{ri0x|C2;PC^K@@Bv&$Y6bw0flq@;kRK?VWvSPGid?l@b=72d= zh|ya%C?%DlPN8-+PSIH{)@nYbphg>Zz0K4nktbuD#Vy8Vu|jnYwA^r!Dt)BO>cUq& zt9i8olF=|ls?2tqiVRI=3rirBA1#B)8e(cb1sy#(LY*^t((}?@%2Y z)-!-%jgFD5P=qp%+6_U}_3pWdK0IVr1N2)NjD+UGi*dSdXR z+pw?S!8l`30ipeT%{z@ciEpq1CsPL0=SP820?c|%cp2*Fhi^zpdeGm!yy{PF*hdZf zS(-!Wcxlo|V?xen2z0h-GuPupxdn%GA&19~fIja6KW%6kHu1kJmugtna~#$D$INQ> ze`Z$m9%6rHR>dAt{qr(I899BeVUTHZ#!CH|GNXUbta^E2J6daZf$*%JLGrDhVe+m2 zHM^3|xcX~mb@unnifkNRx^xcIS+`H)sgrou#C#bk?u}IEzJsJNez|w0cdZSy%&VG1 zEfrYlij`(~VhO|8zA038Bw$8+s$&q1c-!zaRI{p&OW%AvxpHHcA=*0IY!p%0we|m> zSw-FyqIk!o28Eb$S%iI*8Y{DB%YEU*H=s}b{ppXN>s;=LSNI9@1ynMp0Dm!6&R2q9 zY+v3fML%H27_Z!&;5o#-Y?*+A7h+HNAUF?U7A2P~QhLDnl|I3bD3D4-#hJd)SZzbr zc#8QM4iOl9>PH~onovI9vXBV>x{!*znD&7s$&!$c{21+smy)I6Y_b)!)y_Md%Hxiw z`RR9w*ivT{m;8h|648)*8c9V$Uh&Go5SP3w6T(Slq^JZsg&?XIPds{$SvikWQ846f zdKqF%*~#2LIZ$UUiL^>AmwJS&N1gtMBp+vu<`v#28Jf?l@td*=;-@cTUx6x)}|2O z?H>_eUmy{dJ-B%B?mTjyzof5oQxD+p6J+9tOg`6P((KZ{x?%r}B|$l9i9w|g2;YJ+ zasA~K^8|G}?Ag@Hb9ReyNAA(zYOa}|>mAa&?U=(JOAdcI?!V!B0iIMUpeBm?!vp(T z;majq=XtzZ9d8(Xn!mmG4*#E=`S3N&XAb~7|9NKh4<)>^sg2p+C48=$t~$0UV7BjL zgM?;HIa8(qRcfGNAuLU8Wz9OHR$Q;}wW1#D&um|sjSO2#yUFhz-2NR%?}T%*ei%A_ z`GX>!`I>GO$!-2b)XZ%ADcAD|`{Uy{o*#&2XBb4T2w@n4&(JA;v|NVISjprUCvxnl zSp^w?TebkCDH&HXEg34=x(o~%H&a)FVM8mnuxc1-F&SZV&iKLI;Ds!tl3HI+gR1R{ z*5cO8L>V1nm+1obBd%@wLGMOfb9t$3wU*@ZWxHav4zaH?cWXQ=wpSc<&Xi$k zFijM)f(VVX^M(+v#O$V1zzWxcEsx_fN+jLE-8gTX*FpZyzV00Cgy9H0(Q(SXjat81ej8TKvn|+pVBKA^Y!i$Y z7(-8rLw%0>h|cr2`mNlHgKE8`z#@C)wTm_(+&8rXReFjTvRIc!q0MI8k`#2_LsJk} z613PuvKN!gSZ0a|b2uc(IYY$(Pnih|%7z8?Vi&Ll#iMY85Rc*$T8ruvJPh@xe_7^T z)g5wsyeQZE^eotSTh<{$hJJjE&R(n2DFWoS%8DchuASykl>0N)&6ni<77F8hA}d*I zY@yvYW5J>R&9#5&hpN==X|U_jGWOTKO|2=(9~n^rO-r)grq1H@t3}drY4dFtMy1$j(1tc4fG(^2q&WqOK^Wy{~kp4OxJu4Ouj46-`S@L@TmlS_8;r z9ikbXIeZUoHGj`D?roZy14YD=Mjw6QBP^AKg0##lsY{?sL_wT0g`%S>0fiDmn?r?S z5Ob^9LtS2nWdAv1u$LaUBXkMq7M>;A@CcqP_t^XlThtE+UZ@});OVn#%{~9dn1>`-BPJ;!Lb_*W;2;3ZEiqF7_aY1JH z5;(fqoAc2Y+v3|})N61}a1M@k4tIp%kAD3UFvz{W{B?Zyo!Fo_{Ad#1Mhc@l7`5G4 zw|@~}CbE5@e18Xlpx(-@i|6RMaL9BEG!-(wKcIgSGTCUztTCwD{`Lis8V^oe!d#aF z#aR-0t~67>Gdro5~8_GDJ(7`{=bzVU1vD@*vP(E9YTQ4Qja8?mC zC-#JWMBsSN=Y}u&9-D_9%&Xy>!9PI%B*zO33C}4&a%}#`f&PD^^#f}1zp1P(r6|UnPvsiPfUHt#unWOA6?uL@8!P37`p^v@_Qbh%J?vlzTr9gaBK%{sGkf zi5|Amd(fC1?ne{uSGY$n54Vs#c;CRBG?t_2%j4ptfUrSrkX+U;J-6DNWeCC*nv*rC z5UTNVF@_4ch%91{0uq{%5j)DVbs{VX<`iIvyFclC0G{)bE3U~bbt(8Sf>c9k z86j%hyvcrNl!ax#On4O8b%PL}$Kpa#rYx3n%+INARj`JD3g;}^)%UbC5+lA^U%5$B z^bx3(>t`==7e5MmcMGq)_%j*4p;xe{p*>LC`F2OyFTKy(9q1aF%cZ`T zWz8FKWgduRZE05&bC}r@v?yB83}In*^oU*KyJOAZzq7UpFa!9@`D3<;_LLTY=ePb5 z)6@L-`s(ix$QU}i$O4FH7Jolgu?dq>{|W=L@wt3;)J~sL!jh~&C>@nhY_@<-sT=jY zn*=dq;)II+SZEChx*e$gpgTgXrD7cvlf%_S=JiA;fYmC)z_PoQIXoP zGXys1)I)c>+d&@k0EtQtiJI1E6Ma?$DN5WB=gc^0*5(1ylN_^N`yr_?;*|(?kRYj3 zewUjU4z=)<)Y8BjbCdv8y}1?cGS9un6@ZAa4+c6Tu3~&{|cc^ zQ8ci4pd6Rp>x~!YnwSd)2;+)32(L*XleMQ+QBdiG_RtuOLOy7W)zuZaozHGn#nkT5 zJ3a^BUytNg#s6G}Kao&3HMRbCAf^FjF3NECw5mJn<*=D(E>dLU zI2bNbj6idAT?;GsP{n?8+vU*IivD7uoM3qS6T9fW66Ht`#&cKZwNKYI?A`0fJS{d1xMvjNHMjq(5 z9|4BuSAIJ~=v~0xaO_u~J-2`P3J`u8jnV)DaQu%!_-}jX{}TjxGcy~2YU#gXpge1b z1lTD+rx>pRjGU=$o0PPv6)u}aiy_67E8(*fZUmy^CuwOsCf(BO(wkpl{i5KrELixS zf&5Y&-O?}6N`}C%a=Dq0Co(e+m#^{pfmCiZgu$KwKG|fqg`Yygkfxt26FY%H;ZV&O zOiizPKRivYm>H4@wViS0oy414c^fenn6FrIv^h-5^C87Yj$uZPDhCt3wc-9cI@H|!vDY|gI;`N8jsuX<|U#ZIz zAzosZTihA?yy0lcUu0#Fo)P7g~#v) zN^ms(%2(8RJp*PMnQ21I+NpIleW z^3Bw`VGp00=)#6lYg_f`=1dc!ldMHAjNfJ0P~2pl;7jWu(>SpqEHmL;yH!}|^|OW4 z$R)<*@%=^zsC=I>Ux`WTAW*R|YCvj9ls;5OrV`@AM2=}Xmrl%Frc(g^Ph`QnSh-#& z1Fi?a`e@F?J#>krCrds8o-fo$j8=p(F$!I*_!0%3$}|JDaZqH=qw&axNs4XRieBHZ zp^=gUH1zrAT3F}O^S;sIT1w9c2-!+&lx4(kVj8M;uN=9TknLK5s5?TpP&Ny>MO|^< zQ!6+VDzav@%8Sr28*@P!8pNE~&BRJg)*#;y9rD`zW^K7Imp%&ntK9Gg#*DH6kR?F> z(IERDy4-)Kb9uY}IKE2%wt2@*R*;ZB{rXyfl7nXTYJdnzm^u?$Dxq0&*_=gI%rMeK z?5za6JpfFC5D$vq(|rRSVa@HNl}v<|*T(#m=RD1um*3s}`K2zp9L$A>MkqM*{h`_n zyTXWAhs~8|?na{@EqKe~s%4%0Q5wvQIR!fpo8s!5@f8e6Fj<)IX>>s0jZC`|ulY#U zqH&l`|G84v+&$L@=TX-|c^n4}`dFoU62iIc^tGlGN73;ebM#ZG>k1E4$hMjeY*5;{ zg!)3|Mx(IUJISf(eTWqY55$Gt!gO3MT#}E&ZUTRNXeLA%jKWq&Sr_&URuFwjN8w=6`c>3Rp52FkH*<&S;c0QQFnfL* zP!P?vmjy|j&25b^yv2t(NcsgqyouKzqZvDjbM^TBxmiyLb6J|Acjl%f#P%ZPgz_R0 zH#p_ay2x8=z4~$yW9egG9B@)A*7#wELSg1d9jXWT!%FW3ZWlC4k@E#|EDl75i;S-}n~k zvKu>cQ{o?dg~(tr&avHZ;FiEa(< zNYO6h5PxIxrDoBQQHjTakBlJommHSLX%%sy&nlDX-y+`;9dqS*9&hOjr}pW`|APfH zJjPUw0Vnr201y6mn$Q39`BO4AHnnrH`4`vhe{cxvdt}o?xti#PM$}3>q?)5LqSXdU z0}}uZHqT>VB9F>|Zjb)KA$$SxyV%dbdrh=ZYJy->xtw>o-07a4{$5~p;Y~Pm8qIq9 z`|Q}IG-?eRq$@Sc_wRR0kbznuLD=GJ5hE@kSW-eq3^78^B6Z)I;0On3+W13lXGSX^H+OPC;t;38AUPgJoX=Q#n`@DEA(~alo63ucZL&eM31F#1R%^d2 z`ulCM)!QdAKtYsRO?IHL_bC!V<(~qH*njP}=zNdkbnx7}Z~h7W zi5UOX<(cb}X}Lc-s=L*3{c-i)Y5MW;1n617><*K-?LL5bDVQ=&7Y!9n83hhv(_qoy zpwUH>MT??IC+=eNZ_4a3)zr0)?a89q*L!3Sa?@;?-sVL;vHBG338OBfSQLjQj*c3A zqyUrF67Xu(a@J@%n@?EHDNs3_$@VVw zkvh*#s7`8;vY)bDXdMFP!|d2)?6s5YcS{SYpjs%d7gZ%jS31sqYJ&QOTNMX7RaUB4 zM?O zcM;%8&asT8ec#M27+q!>3R!L|{-cOAwM^b~{fb5#*sCq{ay1Yj9qFPrVA60d%;6c8 z{035zql!0~`vAGy);ugK3l-E#F+FEtGe%sMFSYjD!!oYhz~61&BOkgRHVE2K)baut z-(EhE{m9ce$dfBgRm-nIudAp)e9mCm8Cll7@DvF(jg#JK7}t<`1rbL$Ca+7U0i-Z4 zDjvm#Qrr3$eAC^r^<`<5w{z73magY=`vug2%nni4kzPX3CbkIyUL;|CDHq%>5H7xP zZ5%JnW1+e@e)QW+tueng_zKs^()-96hQYW2V}u@R{>d}Sva3(nw5v}by{k_sHdoh> zEbjJ>HtzPWH!lB9T2N>9MMlt-XxG`haDPAgWMQ#5$_0!!T*csyjgwpU^h`)Mqzmjz z!?mV#>@fZ;#FR|t|c?0OT_gH|YB$%V%=%!X>0_2_8oeB>0p1LPuDe~;AihMglPm;33Iw$XQ`~>g8bLGVJ_W}2?mEfUf zFztdQ@X5>aT7L^2!-Y8sZY$DfMy58MoKv5sdfq z3aYX1@|9^w?BIu}gBO814UQEL2958?b<7kX z0fYXqpxn{s%Ym?Sv+EcW(tL<3nOIw^Z97v7a$MZ_E176ptIoZ-mbor$m@Aq@KYvv2 zOd$cj#Abe}Gx0D-zwuEpAC2v8AbNog%q{KX_GWDTp+cV9i2_j;9^8ooQ|1o*C`F*b zg>$iJQ0GE!Ao^3x`R5~7CohgElE(55uFbBw^clrz$8&=;F1ateKH{nMoV*0Tq!+b) z_`Pf8{P*QF(^u2X`|Q#-q-W&P0vUg3bko3=gF6e%6jN+}8y;N4&fIQ}ytw+Q8};Ek z)j$7^5gdMHFs$Di-Yu}DA!Vg|hPrWiC-6>0OVvQ+*3*Y$LuF^$mi~Kv^F!-G$NOtq zH`?EXGG=wD!gv5Q%!B`@N0#^>Qu}}OT6~ps?U4ZVhkm8B!D>}0Yg=2&rTEZq>uDBQ z1~OESKn;@k6$341V{#4?eMoK<)!ifD_$;#)J`A^u0H4neX#t4K{;5uH{;9U(soyVe zcjy63W0)n5(*bdBYu5|e@t8b4{Wo=U6o4OZ#Y{pyH&|@HjkC6@DN+ACI1{a?!RIN{)j3)+|r2e{qa&8asc9n;)w*OG55 zmQw1^;kkM36$*u_@b!(zr%0B$g!5?M&Ta`R+7J~v#0elalJN(mw>u45Z8@)>FvNX4$tFBSHDrKy-F(TR#b{`>HDG+;!oz52JUl@6h=DdO z2EA!@dBx-1KK+EbRJ*|g%S`O=fF(j2!a3)}qJ=I@^s^+^60u$h>$WKgPq+QHi$2xi zx5DT*)$ZRla|dmXn`0NL=;stO$+GjvW!B+=Fss)0=>cIVFO5iCwaO;ZG{oEh3eO{{ zl}5pQOmJ&pw&H|{+e)1{0@kX`io%$`u+c`73`h#5Tj&;%6JjsY;oe7Ig2K2}e%VPQ z;wJ5P1Qm@g=~;vY3SUv9H}Ir+o1utFg378CZDM|a*PHM83I?!hm@_u1P-ZrUH?y3~ zm&4*nm@!m{9siMz*(tKp5L1}A(f|DT1JI{jT> zr>bfzBWogjz>Sgsg^Q{b1rYSQvXdAnplluzVc0wF_hn&hJOly^{5X~}BqwoxzLViM zN=aGcKZ4s_+BaPAF-xMDY4{X$ly}HKEwlf+KJNYmstX|hiqWUU@7KeWj7nA|V>j^O z-O|b$!1|rJZM0cg&n72eKX{dS5o!SF#o|EE_*pdcvL(|b?E9WYf`MtsB{_H zEpI*RCq6n_2*#WIYMs1SHt#{pkoBN#%^4c2ySkOSpf~SPL>m)>@! zzQ#|l*yh7JY9tPHzMNVfIXCF7lSh?;_-hI1DGBH+6DR*eKktef@wo=eTqSy4>8w(; zCd=>6T!weJDt?Pj<*F|>Bbo2l*8ZOhCB*3*TvACTVL@JGKFZHDu5M?$IgvLoZ|ulC zmjN{XpIl<g+gEH9Alp`_(x)|3+dTA!Zqyd;BPl1eBjr-QzMFXZh3<#Xk@7@ zXma~eJnK^kJs3%4lUd~wxL#J%$g<(z#piT;DB-5@B=EyP9CS5WPS-+s0x)qD0LJ7UKa-K7asYjW}F9tj{ z{~aVZ{1CfQFO2e2l)azDR70gyW*@6Y`JmSMJxCuu#>m0noV&OO{ZCu>h^iK*V5d1lT9Q$Kr*q`5?GijTNRkj zIawP{EgpYS|B;$TU58kU*`s$(o3{EarK(LGn>=oIsWLdnsV*qouD?D_uv6nm+_=BS z&mcpht)eV%;OVbN%elz><(EX7MLS?Etc+!v!7q}Rd`-$ zob~{YY#8?Nh9KT4w#fdsRymkae-nbG)=Q=lh8(G@r% z8MH)|4e1!8i8g5ifZCk!Xiy2cr~uij7{QO3u#`+I;i;GrTab5}@xR38kmV%%vP6=Y zAkUdLCF58{(`2sI87>Pe^*oh`?%S$3DwQ{6hBvQ|un(@jbYr8ccLr#y1XmD|a3#Kv ziEpmU9TH06x+hKVVs%DpOh&6h(lC5}HJn2_jrj63C0N)o|1GzXs%9UovvdS4w)`}` z_b0dl5iRGJ6jK2PSuUy(x9Ns*c}Bi{upA%_hjw+x`O`-h6|@#_kFJ|ToV?q=t`p#( z*}noFPVN6g3hRIG1knE99!^7JV|!OS7r<`7zr1giTy0GM?ewR5E(hSUew1l@^{QE% zgMy%nl+s$LiqMH7Kw-%s!_0gzo2=EiPOjOyl~eE~^RpyMu-pRqQ0)JIjD2NToypd0 zg3BhjySrO(myNqaaCe6w39^H`yF;)9cL?t88r)q%@Vhf|znS^wo^yZf=ix8iZ}qCK zRb4A})l64*`Bl-uNUF=O&mLd$>aTDP=2z^VlhG`@-Ec-COyM#cJBsy$bH{D>OM4v|9a3f^l-%m9FK=;kE!?jp z>KFj*!zgQ$rJPK7R6OObZH?Dog6BEeDw4`Ii|WURVV5yzTI4K!kz_e(w;xku5wYG1}-NLoQxw_vz7+ zZPo@)<9&)EA0+8;xOb@|szc8_)u3(a?9kX|u&@g3>6Dk}vqg!`Awi*AcK)Lo7@)%NkS) zN7?`yCP|d;UXnSo0V9C`B>rM}r|g-Bo1{HgWA`7d%!yg8Mk)L|`J*hZaoZXPgg0TN z8)l6=n6Vp@ErId8PE2s})IIyKs_2JK-6!|%{vNI;wm}6l=oJ1xsNAB%obH8#4#1Dl zah@7qAxKdZKVIjDO?!P4M4sZAbB>y}qrw+EGMkc{7_&T+^#{jyKp>G-ph|_7;Z(c9 zBW%?W8cbJC2$B#^seU>yEJn`tshcc3N-=TQFLWJ^VlG6D^TDCKLpX`Do2cmp?tN`l zXvS|ma3=8Jp>ulK;{t{CJ4RIAEvekYd{1Vg<#Do)wTEcX@3*Lx)p*TUVJ`V*{zJ0z zE05y{NddLbxYyP`$pwQ^t_f?daRst&Mfn+t3M={XR8|PnOosvCc=5BTyv~+`ZHqqC z!Xt<%-2yArx*$`b6Xg9DlxV$gNY(+zo@4@?zCj-d;53!eV#bsx_y?dw#uoI@JtMRO ztfBn_n$W?$?c~$Oo8O~7+Cm9leg!=W!~gZR^-rjB{AZ}Dg5^x`unK#NU(R!TGcgC# zKiW!(YTz-qA~?@QjCP-Ot%gMu^5CqIWt4?QP=f=ifaua;D{+N{%ama8K9lSoAP7n} zuq2!G+<^QkxQ6$#sISw!n)WNqnEiq9`R@70d^WhR#IRuo&^$LpVdYQgRyp4~LoUQg zsv`R&<#(nF?#3r$A_9Zl7 zrv_JMS)zwg;kLl$>@G>$x1VJL!P4BXffA2VgiKGMN}?;Nt%&}@|I^SaPuV$Wf@T$N zj<%Za_MF-6XSgKn5@GlREs`~A=Xg*B{~@Arh+0N6PSJCx@CmitUNr_d-`Efi9vav~ z?pb6AIgZ)-ttfyI1H;zjlhN8q7<5?!_3q3LH+@;+4nlX=iyL~k=a=Etd=rCQ1kf_& zX}v?NQ6HkCb+L*8qCD7m&|zmC5TBt>EH+jwb}xljHz^T#O+DTFd`4Sa>MqXqOndL{ zBra+fd?8puW={H|`hkQhhvdz@)1>Mv(t9P1+BbuZmQjaHhrMsK>$%y!}jSl~mH%i)BD(l$;xkBK>A< zO`#M7-^&u%<->W_X_H%ei0p@@HW4O*Zx9F2mT#6d_?Y@LR_ljZcz_Gr4NGfG;m~cT zyv8mmXQ=6i&1gbZjq-l@9{X~}^Ln-_8c7eQAyWiDrhbb$mPi|8b8iM{-|sq(Q!+G) zP}cOI#qvxN+=P~9ijz!ZT3JV#d6Wq}$ete59McbT`jtxr- zf#m+Ag`aPRT|szX>}^Sjf(9PiAXIv|kh@uhucMt-)&ESyemQzpX7g%!p*%O>Rz0E0 zs0$WdFl}9PqBLie#e-MI_(DEN#VR(86q3*M+}4d5H;h>?5(jV9#IG+fq~B;_`g24# zVE`Q}@~tPv%}%IvUs?_!C{67_@ADZ@MsG)vE$N{XYjut*80aP0?k2W+)xOCxtU}Z)DNkQ znj0ZDIrwhU^IIcBQw`r{1l+{n`J2Y@&-3?RTS|X^BH%B@!P(0DuLJtPA!ff3CB{{i z217&%duPJ}ND=-Pv)85xPZD7|j6Z){qcexVI zS5x=v!(Rm9V9E~6V#uVSR*6>|1@ zsyAT=)ZXSoaVcDSYtId6oCoAKm24r3uhFesHak(YouqT@Q3>7_DY2Y6Ic z0b^>|#+em|X3~3CHdVryS>#@(Em;BeyH$yNGgk>F@7myWZ+NHQa=cw zuW0~sZ74-?imFZ))?&8ib7Xs$W@dc#4kGem)Ex(4mK$^w+UPRzA8 zitjAcJxwqX!>2&E^1*BKYU0$RfJ^O7V}o0wpUTwMhwo0(X@jlAd~vJ2u87*3GQg!+ z34-(YDY@LZEqdWd!y{;WOtdG@<|&SZR1rAl2aKn#$)qALJS$&b+pD}bqt0^@RW{8= z1DAqFVUKioi6ihZu1pWM2(nOED0kdvQldHzrUkvU@V<@T0(nk&xgOxa1c%^nu^|5h z*uT1E{s5SmrP;3m4S&h9j#OR!<07I%Q&ET+Bcu%Fq?;gdM2rQ@@t!Pz7mo>Fi!tM~ z!cJZ-;*+>eZJiL$yXb;tX%9X#;;(6`{mKa9$aMKJ;v-}+cKW45$Pa?XP~an}r5H|6 zm>vaOHUNVO;5ajc2Fm{Wo%*5@fh|o5m$w>-#yqqTwvAO1>{<@vG9oW zCB1T;(I8({9aNynxZGb9Q@D_3m2R9VqU`b>Fd3&xZdt#H(_B~FAouibv&A`AV3I{2 z)EhQkY7?N_x;7Qf(Pm}st;@HH_}!;YU&ugZM6O8Ah!yxQ81zp`Gbn1*AO}8Q0eQU?=Bg@(I)RrZ0l*T7e0o z9~u8nRXYi&af8@6rRs;rknG6qN%Yy&KCVvL%lF!(TJ z<2lGeY0+Yvk$%k2TwabNx$(hhgU$h>Tl`B~MWC2{%*|2eT0QT4KMOw0Px<)ehOXLu zLIS_|!j}^N2}4Db_i#Gppy~lLmLULf|DB#lcshc|KAM?|{7Vf|1yg@zaCFr)z8PITiApKW+;FX)q{t*f zo}I{a0X#AZGk;7jdlIQw@j(XJIjP_sydaqT{{-h(p+AQ@Wv4pP6>_r5j|(QZ%^5tD zvkUq8KD;qR{ep(m*M)+nh0!f`u-9_y0?*$SheH&;5sDM$#2;zI1HuG(7HTZ5tW;F8 zex(zuHs&U*_BXnd5AT)7C=4d#}L z>Md|}jdR%B9nj<|7EeG!NW+%3bzOfwYgZdHGeA^>x0B)gs4xv@4RT)0v+TpqU8dq(%SuDQW3QxD^3jtb_pB}eP9y&G1 z0zlq8+1;_Q^M$Kr;%dH!v6;j<5Uhh$_!KgSkZRn<;Z(mE=JYkmxEo))rhK+1^IDHEW65d8tj?K;LSV7e~yI2P+=*g8b5 z0ji+nd11}_G+TAFC%p0B|I7qE`vvwuzbPB_5ZZM|VFQGmQtV9{rmShLxzpHyUlI{_ zS|BSJsp}gCB!K+Z5+kc*Nx{q7L65a*;22+cve5-4in zHLnkXc(PzQLf7!gSAjMTh0mtxd(d*ravVEhl4vUtKj*&j}xQaY=9WJT)u&6!DJ zX_$Bp29)gI0QLX%e*2GF^NZ|@I6D5%4LTB>0EaGuA|O^7gtWngrc9Ox6wZTcg=R)k z(#ThW@mhz*b_dunwtPMop8)4jmC0G%ff2MR_4Aln>&Gz+1VwJSTC;^kvtqX3bc@i7 z-^*)~@H#l-0oOcy?t&)T(FhLh?`NSx9b_rJ85|7-O^l<2R;My9%n^EUSEMTzaC?Y| zy_-qRJ>S$omTUC{x!T5J>}46GZymHip5NAGi@0zey>yFL+XLG2Q}K0rC+g+fnUFF= z^}CHyXciJxBpu_E>6Q#6>G5$Z=-k3cCD84J-_a5v4tp7p2bJd7PGaGY)xSB#V?x1u5Ix7ggYTIF{F zCTe!ESe3T?pa-oV1xqY*;Yz--Xqf#Vew6!32OPj%XrE2!UZXBMKxh5X2v>0}CfHrE zM=QY`x!Fq?2doRVy`c&=Egdc(Y!8F-buzvd$%7Y0_8z>QRD}(=HgV1xcr!Kj)=N?_ z<{9C~qC5^+nP>XjPevYJvJG2<=AqC?gvijRF7SA#R5^c6<#Ya=4t&OgKuXYNu8tzp zBoApBAaDGjEE-o9EVcoWKY6|2ODep&r~SJk6QMLV7T-%1x0JEIFanAN%eN2F2xhtl=; zYjyCSFhfJ@5^W3*C3(#Z6efe93&4h>DN~*hC-UqJChXQ3W=hP2yO$TVOr&Kbeth-v zE&A&trNd2PsjtWDK8_lmX<-_J_`1j3OH89(pS?wliaMbF@?-k~Nh8`{iYoI-+kBGLp$~m>6-?hs zin$91bcdcC5~rZM+OvU8^5?H=p4< znEP6oif?6#J=#S|N!PH&yeWGcV3MPM+AP{B8)?|r-R(HYJd zHNR=Mff+etD?r#Rw0WV-tJg_9;v5bi!u0^spYL3mCP?g(^2Z_xZ#f z5WOd4+oQ9VCKQCIs6&rQPUK=Nwjn<=8)mNrkyeK_gu39k-`>Ob$$CnVyuMUUjR{Mf zOiH8cPR66{&c?e=MlH!7@ruR7XbL0SC6jV`O&&y{ng1Kerv3z(LGIp>&5I)_|GYZ=>$@A1-vggrewVQRHsUT(RaUV}808s@CQ;>kSlCZtG}Y^Go#CkY z5SnPnqGxt?Uv+4$!b4wV&?7@({XYl|yV*mm$g}6`3-lU}so=9O+fJ`vbgt`o8|E7XJjw1FW1n$YN;G_u86#sgOPGSI@FMm2P8vAF_TY!>EaNmaszy~?+=e`eKe4RbH+-QS zOg(x!rIos&oz3^;Rkj6R#NMO6yN{5zL7zFwyRptU>V>N4CHc;uLqZ)C;e`tfVWS8E ze3}-Z>fSBct@BeX4dn;JLQpD#4K;8ChVNOZ;G=Jv0&AtB(?>e!p@?e7>(@VI5J3`Q z&RRWaNr6iU8u0u8PqAY&0LhF_{t_>nz&hp*&d&Da1)uSkPWY&ipjo20FI*5E>8SKy z3i9CVEfb&S6@02Q?Mio^8_=_76?m&_ARm95V2#5Si=G2krBeQ*D)p}oyua`KAHm;$ zH1Hy$+7Q57A7n9_-M65%7#7^Oiy$izQ~NqS<{2&86U`-VC0rXGOYj3Sr9oa~W(y0; z!zj~(&nodN4V=5L>+f;A;owdsHB=nn=g8#X-3#6Xutcek4JXXUSrkg^u*wmeiNTqu zW6OoBU~met`^7S7Chj)bYoVQ0Q{6Yay~2NE^lXvEatAz|I`?%)YpO5aPyGV6wCt8E z&o^rCy|(az)aN_KMG_RdX0`NK`vn1Wx)1V|;41P6{{4^R`=1bC`L{ar_l5W);8D)X z{?AuW3N63ZB7>7TvZ|^Y6CX(wyc8&%bcX0t?>12tO%#}JO*8_6iPxn z4Bwsgqkn^dB*yYV_CcRsydVq=j0ubZ6O1_wlRJ#PI}DK|3|IO{>j)a3B#b%ID2pTv*4|@+nj{Qq_TzR> z#@C*_k)DE#85ES)3G{;l4Cbavw}Z?w^UT0epggn8$gCp6z>MP9I6Y8~iFOY8!U=5m ztGFH<8Lrp7G%oaBK}Va;&_IpzK;8rIJ6;bf5L6TKwg;DUt3K2qSv6a#kI z!GWfB7KSTuN#?L|{3s$YZ(tN+h``?hEF7;|*Sl))QvCeexAFhQ_W!aJe>)p9|IB-) zZA;aWr5%#MKxJT5v5^qQAt>Q@En?@#xZh5QPN`_%i$U~w7fg`Uo z9)-ji6|c|QI)*9Do!sA8T=X$lW8bA*xY^&8QKUXwu&fJQhlX3PCdMQYR^qG0JQ^SA z9kn4@oOKc#tw#=Tb3TyNEx$83SCgB1QO|hpvFwyyf1{o17mOkqf$Gf>8Kd!*_N)l$UhDC1W_B%~&(IA03^bKk50MzFLCXI3$ABwN2;V{Rfm*6Rk z9xhi7tg=ngw8Ci>2aw}2b-lu@kR9Xu;N(+%j7|Zlq>C;Ef(7(^TVm!kY+H^a>$uKP zH_Sg7E|YsCI$%S(eIhXs%Q{ZvVnAu)U(3@2WvvSnl(6FxgTADrtTc5Cfusrr1DIIpd~ju{%~>80nRFWQb3D4nuPOWe2~w898R+p6g=urCG}v2a!?L zPSWzFoYv;Kmchkym83855o6NK%O*Z zqkToWw}#EOYAR#)lA_4olq8D&CRt9=(I?jo@R=|XdM<%7$jrQciQ6cTHO`nmI= zNMJXMh*|j~eq+9u_e=Rv@>Kg{vVpZa!j|L?@;HV74_AlM0v!^k_SV}mURXRpVNS9B z9R)><{!shcw+5^(!RYf8>@_vdps2Z^qOSLiXH+8-kpW^88uR6$-K_go2*xXO zJ?F3B8+f(3x#^hF`AH+le-3~K?t|@u!@V7q<&+k2s0MH2GxNr4uD=>Z>Z#!Whr>AD@L$mmr3|kiZp^s4LN6pwjGvYnC2H zqf5#}A09PFrg``}{eHeDX;ef6_!S%SjwcrooyM+8uDHcweDNyt?2+Q`>eo23aL?(F zuh3rqtRhQN2fjS+yoZg`LLZhDn)CJC>4pguN?aT(IKR| zc*hR&*s}o4uboN9Fy(0zcDCNmZ@y23r0l zEWJ11Y9E8{(@#g&qD8oJF4pf$KNYsD3u38)vDB6o%Maw%nv@`V6&Fu*D=bqVW<#&= z-zby?ReI!vi>uGgC=MlM37O!8mK*Q+B4ijjY{Al62p(S^RW;CZEEi5 za`EB8jfL_ss3Hz1`i!S;`vmJ6FCKrd{HY3~L+souA~ zSP<70F)Sb6xCV4h4hC}FD<2%J*k!%nCunwl1NLF@irWxdQGq*=4gG!rneoEtK${5_ zaIj=bGhFaR${Po(WXm(GH&u&>p2jZNF=O(s8@%SJcABI}X&1p9;&HIu6t~m%i{1pf zDXv(A|q85Lfh+T0(A1?DAG~9eYVE z%`fq>*wLdAAVRg=11D>Ki78ioUKJN!<*Ct`V)8l}>f?#fSdMTWe79anq|n0=1D)EQ zQ?Bm_D9#W;=TKLs;qfby532V<-2q6AV)7`2(LLsSOdFVxMu>5UR!9;(A0tq<^9DHs z_B=(DG&_^O`sClVFHNcIM*IJwlE!b@b%emMz}aX{4dvxKVhlh0M5BEwKBK* zbE5KuXM!v1E6Rd5DoAQWpS>8C&yRW|K^BY;-NwAm?aQdzRhb_*F(rZ# z6xvwTQ&G#0X|r)KbI9|fpiSvs@9t6t#xGn60BI*hCeQb5i4MzJcX%k?N1D52$qVVk zTrS;{%W1AlIg%^AkTJG)VNN~2+6}2maQ7bSC_5KU*Pmy}o|iKh#Nf{$ydX>jT}kzw z%?EJGL%GGz?pE2u*&qNV5DlR0n@SPKMI^UJl|>h`GiISn?I(8L(ru3zidgZym-szQ z&~xZ^iUWMnK?3uc|GEABb6ftkLH3u^@-J6Ec{f)#qd(KG!P6ZeS#dJHEN9o%KhKng z6i>@1>ej}l@%;EN;ZTZ7YPPOZ;B`ez!HCJI6o8%sj48ugoYoXh)-n} z%lb}~{p#3=JsL2VaWbEdjlDxK=dwW3nDzbCU6D@;>JTL?OCxk+=-P_|E9m&6+7tm= zhEAKJ^m}j1*|;x|L^oaxcO+2>pts>?#|K+1 zu3XASKdwJ~v_x}%Ru7r@IsBc7;yYOhiJdiD^JG#e82E=*zj8aEqG%P{^Q1Q z{TICaWn=zGef+AIoC@?$LfXHg+Z?!KF(UHhr0tR%M#Frcc`+%!vCzF~5kpOId+bVV8SLo5K|?N}oD~CE zay1O2&@Xb90&v|E(_30e@7ihISAwk%m3RU4pw*HinUuUWaQ?1PlJ{&q#uqxN@2pu1 zNTf$eq!shbJ?TY`2_=~D=@pIFO9o}{o0-%Gx1&@my;gxX#D!VGuryK-4;er79(y_@ zI>@EdX}^cdxBQ4QBC~1Tq}EHWZ=Y4`m#?e93n0c^ErRb4$L;gZCZ&y5EZfuqI@5)k zfjZOnH{aP^Bv)Y7P9vDby*6z?D;_%HUqbJRDZl@OQ^aJLqfML6GF8eRVd^57I#|(s zY7veLFk}e?+{bGTP9Rq&b?UE}XuVAzm=cNh8o9c82;K>-a|3m~jKz`qqnet0w-RFo ze1fqJCSsTKc(>#Ms=QVK#y?lJHz0lPiK?<%rs_7WcYb!A#+V&bh`J?GtqcGUjidDz zN0k#6!<*@T0E?^lQchvcrqtj;@-|uSlJEIn2#*n=0m)XQKY;+Fg<&K^idW?*+Ce{R z5eF1~#R`J)uIOJ6MM{1yTs8z~LOkkCu<&9$&CD|4)#Fp&tlqbiO(9txqyugkq3_WI z>L^9+;N_r~DmU?F^^ToC%&wS+3!xl=Y)JLGIm}DAWSt`1Z*}H@6^d%t1RmhTfwf$d zIc8Us)pPs&4Js_S1qCm}MSmYzAHja^$KTSYpdCzAgTWuU$bZbr_*Z-Gzn?(r_BQqo z9)AuR`7>aa+l~DsT4z%EIEQFO4*4T$2rAtB>DIoIBd(^=o>R4VrDB-@Z+t%pDi`l3 z>xiqMK%R}ZdYy4cdwIS0hEeKwM{wFM*ULY*CeQ9MN7IFBIjL`6tr=te$QvZ5TQ&e^ zc2Cy$11?nTa{&*TR-tqD*O@tTjhafLupDMlT9}XX53kzU_t?men|QAYx1h6#!(^rO zfEFc*sAA|m0=MFyvaZ3LVp~l=~@ z_^rJS`2D?#Db=p$4(c@vQUUHXJ1TCuLLF!MIApNbx1qjMzl93s2bgL^_oHWQzO~<4 zy<#bBUflnyH2hNs`_~skf7=*0`#)X`6{>@e9A%Ve1?(l2;X+0d50Tb{01OFX+FOVs zAUax8D{ghItbVoY;3;iD#~`dc2O9C?>t~v+41s0FgnAYEX?7R$F`ui^k;T<_9sZ9{ zEaBXwwHxaA0iKLW5`{N7gro&uDUx0O#jvUiyn zALrZQvvLFara{)rsjbs(5gcud@x)<@9L=z0NGl%cego}Ec8ww((93f!Ub6GgUyQ~+ zlnnXuB=?$!H~QTK)PIV%j+-3?*2b5Xfu5C@S+0@+PZ!6b-j!5t>`khh7L@J7jPe3* zWA}Da*qWugxYkE=5(<1*yXA5&Vj@;472^_OskOGe-nC1NYlv<3HiR z;FBn|Gy++@tG-Rh+j)Eg)M-h}*w97bGL!XLlv-d%ovjzh2#~b8!{jG79mMIPvr~!TJ0qW)^MKw-TS@{0DNFtQ14w%0v|4wW)pVG^O202;LPUI+NM0pfQ#&_5;jI*Hrj+rfL*wo8xCjfid6lCT z0&0PBO(kFSXF~SJwi62|P0fC?iS)>#bh69EGXuR52XXI(`Vp}m!$BleM6z-U`kl;7 zL1i?U(q6R2M5YlW7KD|otvv)H`movkT@{nCrMj9#0?)r`vn`DU_A+2X&Gz0qEO$Z zE89k*=4%mI$pgO^oK1`+;If(B=Oa&iI=rr|jeR7=U%BjVDQkH@F0i*&miY3#H{S88 z+}Hz6bs^&$MSYJFRyX-hL+0fO1<_V0PFC2)0|gyCc)DpcV9?pEMuV?}-`Efnq_$|y zqi1vBJ9@ldYe!JWn0|x=3RfSt$7-@al-K2>bFOLBU7bH#sg?zxbLKBjdG=Ffe-cBk zygjwF3#_MOgOJ$O$xP;gV=_Bh5lq7Rv>^j$I&01O^s_1|f7$i?*|UlpIa9#d)ueAe zGO3drC*g5ZP^{LLQ(CUbdY04WT@BL7#EeI;@cYL^kxmU5(xbS`9HvjA$2*_)9BaEa z`~r@8M3jn{-`qQ63&k|0o+LW22qs<)JL+U>RlP_*lXId@QKj=Tr>AYH;n|y&NEHvL zUEJ(mhUeqC*!29MZrDngCcL-|p+gv|;$CY}eg1BRX=$a>GOat=q$DqJCboooo`#%1 zy+aW|NxP6W&xqV7uXo#$;Odhxm7=}R=pU8@GRG4-($&~XAx;0%KDTBm(M@OX;n~t{ zvocplervnpG@myqHm<}j;HNvkypqGm^pUPR6}0xL18-eZ=c7#sRIJt$qX+qT5HBmm z;TkDOmK=pfe}#-Vf7UdmsJ6whXwDi-zy{dsrKjWsV}-fLQeg~p0G0LGGs+H=`CO&< zjWs}1@k=&l{@JDW1fGq@Y2})Mi5Ah`lyM`Aw9msi=WO&w#ux8pMTXVcr#A+?()~o- za-woiruce7b`acq-44CEvl>>jaKfcq*!DAZu|FmP=L0u-EkY-5x{Qc61ChjIgTpZP zFN=!dj7@WOrmwVbdBn!m+;6w#l6{j}Q!J72YD~3rQYQ)MA7B57!n&R;Qof0349c+K ziyq!)AdpMT=TJoa)HMZ97QBN_6P!*R((WP#_Y&YCc^^KDnH;3$3+xDKNh41ejT5dV zyHu0R0{=}0d`GzqQ3)KnimQ$beOi*zx`6i&K8kb9ITs-7Kvv%GZ4#;H$12G=zSiUWi6zKk z|MnFn{9^{)mx&B|21b8jQI0|_3JDV#S&na6S7Y}JJY5DJgL~(UTyQmfLmmkc&wc6p z<2zPc8^7t+)Gx%1Q{WRr8}YAqy?>q<|LQ3DOF{pio>roUuIevUEU6Z$7b78k2A`YW zu1Q>?bpr{3MkffDNsuiUi4zB{NE_veul+uUosC!Ut>D8t^Tn)Yf4sb<6;Re|;B@fx zZtV7i$71^V{`Lv>gA+p?3FwnDjpz28=+%wT>%H4g9gHbmwS1YQ@W3e{8S@|pf$^fvjshazvSWA?FX`?FvWrn6zaw{1!V0b+mr@+)Y zQW%#{QD?cvU4ebRHZRvMZO&a_7&m_l+&}d?$wFWqmC0|^0>3TH~XlY zSqgU;${PiPf=iBCM#iZbun^4^c1+Ox17<=Ei%b%fo7(r@cWJD{B!s#~cPXm~ufDl) z*u(vfjrnk?{yg$+m-+siAGxKYr+e3g_7iS#7a6&XUo?A(nftRFh9+cES11x)shirz zpGy?;1DITgJ#$II zp`n?p24XBr97?+JzR-6EFU{sM5JU9KITOve@aW@z&oJEQJJ4XiCK&JA>`ThZmvc){ zY0@gi;#BASHq1(Ck%&*_D{Jf!4l_S&)EEC6_m+#=rXfaDqn&`_{k?t3XqeyqegD1( zY0z7pNf=Pg^;{53g_2e88+FYQHN!N_%iN)9oZ<$NG~CgRYt66Yrb-`usC9ZI^w4bf zoEBjjl+RH{Gnq&Pd_ELi54=CgElaZ%|O+{0pYGc`?7E{H}kX)Ofvxc}5n z@LAxoN+v=LjXk8Sf5u;Ax;X6+y>S<^FJy5i3U0@&ZFS8W=l~@q$}EQV`4!t|E^WMS z5mOc4Y6VmusQlNaxl5)dN`V8SH-pU*=SPhq@0r|(`ukQn-uUO^xQhC}X>xIE^rY1z z;64z7?`E7d0-#^7^brc3)0d`+yO0U+Um{&9aN#Gn{pORh!Ex~Z15>lLGV3rPQYRH_$b5dOXt5E!egu^lG8g`85v%PV^_{aV$D2EO`c0hJHIYUg{Hah&p&mYL!qd znOkws4fM+g6`-#$%V8uD>5(|3#<2Z%=vQiE+WXw^zOe1n#cZUujmiWIOUO?oSme4H zvdiHKSXm0kmG6CaWH?<_a)C5Z?e3zn6epcOktz)jO7cxULx7V>9mjhLzl!BT@TsRu z$mI%aJ*71B-1FA#+MU_oJ#`Z0UYwBy)pfL1_{A+LOC$-Ql9ZDm$1iTGg1`~6r@e1S zWg=5kT#h_)mYs42wB#fbSlH*xS-IiWs9)!2G(DSh3!YrjynMyy?pU~h`L!YE0HN() zn%N!r_y5++{+o5=_YL_=kk8*{q4kJpO90t4As9~hxl0wa@ zphG4~iNwHiZdjdCGkt(fbc|q}3^=S;`LjDbkngxg$5B)hN*@(CiP$>Ecb?gvz?yp) z6zgEceEuj6U}TL;!lz@@Bh)3_vptq}bK@mAa0-mM5wL?Li_(1>MAZ8>W27skau;M= zcnIi;{0folV^hlIoFbO=^@i2O<0og;%9r`x_VCzEdh7TT(HC#xMb^WIrcN zkwQt3eM!am<|a>=+>7C!ss7Z+Os&W@RMbr3_D**(rOUB;U%-2K20d?CN#5#dSa z$A}<=JLx_@o8^SGcx%g;@IbQ%zXu#t*HzWjyj%RmdpoO-Jj3{`qnGVE!3nWZo17A} zbYhoPt_9v*DC5{aaL~*e6f7he8v0HI*5V$&VS#V0Y2K6qAj)AoPB}{3Nz03(LF7VQ z_v;b;qs36lOz!dC<@)o4l4Q7~FNb3XPa*S`aO(9mefHOJkbs7|* zefTNU;alVBifk6Sor?L#kn}0ehULSr74B-X=io>ODghz$lzo2?O%~C7Pi~y45&}J- z+ih~%zhb&$(=}%mizXdrUZuC{GPVC9K=kMUrF#ULaDaXXO_CQ0ROLfzY3@>N1hc4u zC5AIb7T$@WQFHxnWqkAX9IkUQch$;q`=`5`rF-%Vm6+h(mqtNQaJYg4f#>TFMjUCL zd~AtE^&Ugtz#g;UIIbtT_V8^}^Q$5Ti(W1#J_T!lvJiJr70OqcQR7;CysvMs zpsU_kla&O~5JsXGQl)&GMaf2l-y*+27I3`?v5%X%J$#7JV$R8T;GJq9IF$V{n-oz? z$%<~>0yN97OHUddS^=!IXp(c%W$O_Xf&RpKire`F$b5y>r^ zWb{u1U^%9Lu+Uu-c_#iIM?8Lk4Im#mv;>N;lAJv5HkxV^bDp+ViScVW5N2L>P4IIx4{lQxHjB?q1^O z_%S#cbYA|MGVu$$^yC3SAfGxDN|R$zMsi(7dD&%d(Q#<7?PHrvXOX?wE~7Jy(L5X* zgG4%Zev`z+IpJIA8-fMkkJW8gKb4)E&t54lNtf@As2gFz5EQMdjS3LWF{hIb?KnSf z?7NnwO4qS`_eF!~s|z7#JY>T0$k0)3d3p(EPnTFc)fLdO<4!%w-)AStFijt^cQ1(l z)z*w2(r9uVV?h}u7%X-cWu$hee=O*#(bL>ExvK1EF=I7AI#aUp7`q&T*E|TlHIC0M z%bD32pFN*0OtT5Z5Qk1=x0gJ%+ZR8JYyH*Nga-FDlLw_3F>$?1j;T_plEdI-(*a}w zStPtpLLn%p@2fDy5tc=c*Ex+evlq4Vm&QVad+!oO1v|d6coj=!MfX z)`@5cGW8s%qf6!Yn66gj_a^FGQta6$F?8Mo*+uyB+sJ<)9S1<+iRuI*uw}zU|pecI-80BGpgX?{XVqTdowvPI$P4g$ z#nQJLN0kv;gyva)?`*0kPiZ!`a{zzuY(C`b~?rQss+m55HC z%>0B0sv;doR~(1~xmQ!7W|%U+icw3d5U_I{hDG9`&4#|yuI(|fX~!hlhJxFmH6KhO zS(JZZ|E0v>JAO(d;Ux;~wWwT21$Q=8kf!j$LLhNT2YBLbpsut?N{-gUwVG$W9efW; zD}MJ7v)W_%{K}V*`5Qm`Cs6;@aA0X>>-f8W82lne-O<#@_0O>KsH$JqNpTBKyx+W& z5;Ii6g=8Ui->8Z@ddPWrMq^i^!8TIA55$!Awbd@zFy?zF)#LL|uMI=nKT+{x3t$W8 zwrOCSaR=?%5}{}4J0%2(XcdU|=c$oRvXz3r*Rn7cq;NLG@)E_cR?61TQA z^S10Auhp&j{yby+K*=v>0wsjBiK4MsUPW8ol-GKH^<;^s=@dEY#>KsWV{2c1=k=-( z!@kf_*Dyy*hcVfv5A(GV8~}T@XfIvff7-R4@neua^@zsuj}yjeQaN?uMSE4(#_N_Q z6D)$7MVf@C`c+#!3?<*AVCI-sQ*OH#Hj$bpd0xh5w{Uj7(kiQZyQ?Y_^##X1)n)V) z`lb`f&IW?ANv>?nktBUjYn}?x!rCQkyV}Dd2jx4xxsuc3RsXI6naD!qIq#*}G>pnw zh}Z+r4mRQRRYWs}%&LuRk763OC#Q`gtsON^ABu(RurdOY$-wgaBJ;A07>acm7sAoq zyuO6%u3M8u%L`gMrC2&u18Te<3~3oAFGdwjH^~sHMwKU6a}7^(^@?{%cXGSb0=S3~ zQYIx2Wz6;ENeR+CmKuz=li&4NURzssHxMpef848OOqwhJ z%2`DIq$h}Ll(8ChBLrz|<#v;D;ADLB>Cx!5D5fcu6^NhY9(#%42;?>1biDSMfYg%$ zGQl*xBWAlk!OK-lm%M$3mK6Dp=q*D6pP2l?IPq54TL=1px==`5YYDTO{hC7xoPP2s zgyOGE;uuj*M6mgPNPEZVy!W+RxUp^9R+GkPY}B zguT?b;}Rvf#$sB6BDyiO!Xgf+CAaUSo$yP{Iox}8*G^d+1vKYr^z94d8)CN5XUcrj;MkC!uhP98t&`3tdX==4IYOA83 zW!W7VmjYLN; z1RJ|I5ie+Ma=+1U8i>h}SBU|a#_k7_@%3>@>Krt{z2dLc3G`5aCK)|oy?!56vSXP0 zvDSSz@6_yLmEqJc`g_`T6;2a~0aUyAzv#&Qz1sbwS&H z1^^@R#PM{S=ipEUPQwiWGnuP}g+&uyB2pD2z`0cv4oA1zpHgNf{+zf3;re#K z6<3}vE>-WbzH?Z$Y%BJpO-A8qqJBh6C{ev_O5?2XPfQEaHcofiUl_~JKMQS_=wn@D z39;&$G4)wE53X4>wJdFe_+O;umVh4*gY@bznl1WQ(g#wtf~?QDZA>BrEG?u(wmMmf zO`!^j_uV|z*-H>LU|^0o29~F+emllf?K-FH;60kygV9f9`BlPmWq6USE*h;q^GK+( znDGD`-r25y!X;YkLkdOjI?)hqx0A%i;ZfG{D4)+W#HGvxm~46a_IVb zI$!CKH$Utv)kLu0VeIlcc~HRzqnzvXy}=}7pgA0Nt+*1aRcWUYZ}?CHBSark7q{C9&R9Ac7LzlAIL(o_m@vg+?;yxwyd5x>6jNe>Bw zwBWX6-9DhjOdLG7p+C~Ryd&qVBl|XiCVJW~eDimQ*1W1QCsTR$T_okk`#GaFAbngR zhnJ_0XBoNb{&mG>J1j(P+mKhKeE@SR$Rm~2X6rMMtl~P9ALd?Sa>N!X`zy%r2u25@ z(y;;z)Dz3&j^4~6#0nKoApnZG^mgI4NSs^nu-*J?|*i{x5+!ANCzb12~Nc=%M**c$FXLN zO5@!F-1G2r6l|Lxw&fQkKr=h!xX7%s#W+$AGQjxE8?M|W>ovze=iL9W@44bsb%`&x zDkdJ~1Ggi&resX>9Jyq6osiXEH7Xd88f%$Ros{I1GoYpW8efxOv9hQRuapE5l;HMk z%&u~;t>$w&aD&a&`Nwt>=YX6>4_Mgu|FX3Iy^N6khaW-F$?o^l)xV&u1SM@nWPMa` z9dv{!pnGf6(4!nySJ^*u!=)RLrM%?ED4CsOCu_E3XZ%}$zvH|Ky`z4HJ-sMg^ zDFi3p+@^;zriv}A0w!_3#+ZKQc~ljBN8)SckeVMYhEOSw6kfYdT&YLNt#W5M0hTp3Q#22Z<^!b%WLpX~hW z`pRUJLbK3(liR7hCX5Ss;anTMLl({RKy4$a<4TmaGH>AZI<+Fm8pDlOlec5X-PRGm zU9+%CFD3SfZHOQy(I692XNz`)9a3TCsERh;nQN?|wF^0+Mj053K+AeW?{>XIhElr6 zVRG-nRjCfzxRdk zn9o;0IUgwv<1aWaKW@I14k+hVj~IruS-Y%+&exp~$08+tUhamcWOnN>3@CmH(f$CJ zvB=B00n1UVylyZ$Lju>9$|!vN|KRxSeF|CI49wIFV43GD^^ zJ`gtuskZhFF%J}iMt@aY45|35X_r_?|HNkKc!q#Q;d*VaOz9ynL zfG+Lz7ZUW})9W9Px~z>I9P|O0!Jm7Hm6f8SIY1o7{(lbK{^zJGbP5xPMzdy$Fuz(U zKo6&yF(0s*Wa>oho)4CT!tnk4fy_|zug#>pbNvU!cd{UST332-dQU+YI(k7{$a$+w z%>a3=J*mQSblNy>^PgtVl&Jbhs!m82^ltKX%toq6iCR=Dc4*|^;!DIvI?$g| z7A*F9gM=X8e;^LO}` z@4WKNxc`4Y>iS2__OBo0s~G@8H!(N;9|&C(%$=;n2=OCK?}Uj^ z?5Lnfx~-7rDf;yILO~7`;r)|OJclKS@YiT#)AjZg_8)6MCYoG6Q5-sLX5;)EX#9ak z&<3m1Tc)nftQH5PY+4)poZcO^&E(HgZt9nlHm|4?;YQ-75)uP7JL6D-xm|B_{WV;0 z7mfkNn<7quvfm*mR^C0PWb~=J(#FVp1aVhTx5-%s6{nYRSHL@3@K`Y&iVp-47i-Iy!flMrF);;iN{-KPr3Q%6CizKvi%4kCpu& zPip`6rOoxNY)$_RyNvi1z>tU-WQxM|tD6TnCq7X@BP9e5zBGSo2Rh*}lvTFC zT>M5V?*O|)FF#{>^&4HcS;K+5$|yVD%=M$&sEfv6N9UJY?W*zO>+VMSX1;4F5r(2p zS4OuU25VeAaIj^(;`j+^qGsb<4X&)AHeU{C2M{7B*8#8M_K}KdVFOAgs>l|>No0gN4N@O= zuh#PsBJ&Gw8Q)&3Y!%Q#IZp;|nQMjbP+UaJig=fH4o1Iaz082s3go-P(Gm2cT$9@> z`izT7lg@E|=_cwB@92Pq&3KJ_AV4NfptXwbxF&Ag({gXW(uAyRyeg_ zo?>&TR2U}H%6cjRD_FX3cv1A#ZUvo_QxF-SMn4}uKR0j7$d?a{p_%byQ$ zfwaj${#u}ips<)0Vx@1aHn;C$pt{J|3RV^@P&#u)*eK`{&7E?Y0Igd{8qlDE)Fx>u zzpYsgR$j1w&Q7fbW;-mw1HhHV2B4r&3~l$~GCvmg`ob6Bp5l%G z?^i$r9~JZKRZLxFNECDLJzh5=yC;l_C^qy)>7oJT+BsdAPoE{SIh}W|F2mYgS{&!0 zf04vmM2e-{N5YCfm|Mhi;*f;cWWS~mRxU3358=1D*r=h1C7l+2m%q@OSvnCZqU$KU z)n2-uEr8d8vDJ!Qk|laEWjA6bbnlkmaNrW_$ze)tnSCt5`>ncrXLUCqW@?UK^x;m6<1{zNZg@G%l=hkkQq^`xZWF6tM=wE7wq$#uRStr@ z)I2{`b-lr!;7nP!Is})@KVCCr{r>Vn;9d?WfGXl(4-Nl(Wm8F0-4Jm7$zurp~%uF*1Cx0MQsZzeAxqWnbXH29u@+X==(wY z#k7;}=IQPoXcN-IEqQUQ`)6XTH5nQi7kMeUGZu5_=ud3e)Dy8^(RdO9)Q>#=-?ksT z+$4;xyM#~^qD zc${hV(o$3`;f{Ckdnu@1+!8c4rXL22nm@O*0x^s-6)+Oz!Z6@tazHTeu4$P0LKJ2r z*_9e`-SfJn0*q($%Z95@Nvj*aCMUwvQ1sRVVj+}~2d^;(cIG2^3+MOm$cTum3|3QBLo^Btacw7`-JOKJ2$nXOz#hSo(e#urEH_@JCcpkz>=yfVprN>B ziK8%Rb!r!lufqCIIM(~FR0zFoB+S(cf4Ud!sR=Jwr6wAtV?;J;b60v6kCO0ZlOkHC zV^yN6;YT>8p{lXq*o@|68_hM+!#3^64R=ibd_$~eCW?p0EF+N?l~LRdK<+@E`W%NA zoG*S8{=L;!ckvu31wg~ij@K;eW70Nfi!d3tv&vg%{h>aY;bEd^qmM~!mULTFTDg^4 zw16j}Ug$AvZKg6IZDlTTPiA$DvYq05S~hhmXXlqa^3JVd3k)Z-yOw+s>L50RKbDFg z)icKIpYb|>?p%kO$pMr#dMzaTYMi`J*5S071nN{sh#IdiNR9N|2baT>yQg5RNByk- z(0Y89c8dz<@=aC518#(RNog;wvA9S?n}>6H7tq8 z(4ilb0m|iIP+$f~If~=9x_8xQ^> z3>_hia+UYZstG)G9rxBT_eP|6#@rc2yECFQ@-~vDouU+?8u%+DB1kXzoMCv}IN1Fo zXa+l?G528@B7P-Xdi_&+$3~2=e~kWDWOZk}fT3XiKi2nuJaPID(fPNz^g|k#!h973 zNbm^AY?Mgc94=l+SmJ9!nM4V{M4AVMc8iEP3Sct5=mSg8Ao|t~aw!+0zQPo0P{hGr z$I_PR)47EA^pKU+_32d41sscelsNKWx;@%NG&O0uU1G-x+ZL1AR130q*H6D>y+L|y z3(sbGAm!ZgHqSk-DWg(bf9Nyq&8pq=XVc=Z-p%ap2Dh}F%eJ9HRbmAa5+(N~TXHRH zi_~lqx1`U0D~jT{in%l~r{dtw{F$3sShGAQtM;X+=#tXl1@k;PGBM-v#TqJs50nNJ@6#;w@&P6Hk#H=}R1-mwRijii#os~U$!4AbOs6uUL zxSAvCJ?AcTGlA_{-{`O?){l79Hz+(8lr_vWc>7D(LGU(MSZJ`PF!5As9!oB{X11BX z3c1rL-%t7>{bnJRo6j{NZRhLAiQ-nQTgagoNKiUwXv>=YJbYI?6;K*;*ZXM580Q$w zb(?3;DbsBi*L;vldr|0)^&;!;lrr|rQLnf9045>*FRkx4A?<@X93w{Q6!ogGgYw(k zw4fYh*CUI#9&3{Mox+{xidIihD?kI+XbKq;ad#9Yv|SY zr2}C=l&b%~j;?=O*Z-Fu&7Tj6J%8xYxHHAJVbmehdVV61Xzv^OrTwVqY?9>FK_L4H z2G&#UlINBO6x4-`Ua$&ko>mI#zLC?ehq2P0Tu}ukQ9P5CF_RZs7;>kSo_L&$!9qpk zlsY$-rXRX8mvr68qDt6%zd-}MdJD18XCEprD?f2y!3@VUjlhT z5&*Pqe|>4Im(%d~F)=w91Dv54`}{%Z0iH(|)9XaZP>bI7MMY?c(+%%J0Z2!q{@8ZK z?Ty1s#M)k3waBS9?l96r7gC)sd+ezUkzQFlfYnfbB=vgFBsxxz;KeX1=RgzXCh?)} z7|u5BAP&6WYsXtztGA5mIJ<_UI+SjAfYkeZFgH@TNzbrw$}>~RxsU(g)i&nkK+>c^ zeTCN@UWKRY+CXA}Cas=WP~C(ICm3%C%1kjkhd8=AB$;5}?woJ#LYbvlDL@^v!PlV&3ur=oiof`2q6uP( ztDj+Dk(3qp1Zh^0SnG#*7lOW-aK<(RgE&&(&iO*RnaLa%aLw{ajACMw40}JPl8=jt zGE?a34rNaT3+(n02E|ulEc6680WXLn3tdTM1%G2KcPQ#gsf_+M1$0L=TH&W}Y#AJJk2i@+} zDKABh%2}M)Uw_|kQ%_&0UAX8tYT*82z^;ig!=QMIZZnfMrVZy1XCJ|`0qM^P+d%Kc zqE!o@)Q6XUNLN80F|~d4(X)p52xl%0gd!oeE|J|Jm$mf;3p5iXQ+bWNggx&2uf*oy z7Yusx_m=Vg%|9wigG*+}KA@t6|E2H!{hi~#F2jEZ01K2?>`_D!dGhAQM>xM%K^hu3 zK(%7R?5v()MvMsy=s{rJ9BN3Sxb91-Fr+=%uWB9J>u(LNiw zt{f@lHY;p0eZD<9SIq0;dws(AuG~JxKe&&{+vC*|zgJuEc5@*?>kr{dnIvlOMw=vo zy-{#U8b~eFsbb8W{--{o*xZQQC6cIpgq(S5hy7bVYnT3$N& zPPM&GNPYe@sZwvH%MVvc4iX`2d=5>WYCnm}G&)5qMzfYzd}Z}U94!^|1vtxz!{hpaRO*E~dQxvfp1{)dt_8TY8CS6r z)(>hQ%+P@>jvpWOnu#&y_VSIegC(p=GvXFh_B<*L(tFqX&k|ocU}lKsiuc={l~c)! z@tSy&9G>*;S2689$&m{2_~txq9uLn&va*O0zD|}@R_HC0#1X`IoTvZ9N!UE9iQ-Dn zH%;NYd2!yM6Pm|~H&1G8%UwfbrM1B=X%iP+QmD^y5hF!dpLNqrz6C9~jNj7oN7$?a ze@sw8#8v?BF!bv}qP}r7Lxd_n*D*-O@#AN~Xr$r9!-$#thL7PWrKd0mORnfzXo$nb z**WNd$98(`3N*sG;X#lXx#`qAUwlKLqH43D-|RL35tzexyYK6;eVwm+(zqcLMB{(g zBAuP8138mheU-&oxTz3OD;H$22!i}&MW>zypRMTdfgV|yq6|rHC>Vk3w&GIII(PkMvJYoiWzJm1l*<7=j6J)R|J=09}Jr#z18zrdBotx`d+XZ(} zF@_@=gq%#qbg>0n)JrUxroNC5zf6wC$mqm%2H}2O=ZdPQiM?|2tKC5PgryZxzv9(w z5YvniU4YRliVPE#l)|`g+e>qsyD@1CcY=SZnA$G#qU9?$tq|8=@PHZDdK=;Tsbf>;F6RL9 zS+m~@9;aC{`c3@&Nf6pG3Cqe@*5zM@q*jX+=aYct4eT$MA%8Cw9RCdh`9G!NAM_a{ zWq|=w06+F>h#&a@FQi)E{VjhYqC`NcGmw@gk!eZmk804hnP|Q#cZak4gm(3) z0YzRVB$Kn8Iqsk2%Q8G5e9~APBk^ZDaZ7ivm;a^D6Ab5f=B~ZLcZ7#rEK1q>EuS^~ z_WQ7;%+Ptb*dd6Yav1`oIXirW#`QK*97rPM=lJ#y@O_yqXyL`XZBj?pByOq2K7EXy z6uSbop@f--R)uGch+l5GQHEZq5C*ialSI2hmDM#|wd(-wc1!hf&{!UD=Ok z9%c4WT-eN&FAW;DEBpr0ngI?C4#R>ZOe@`BTs{Vp;P#Wcpt5VR0^x;PV&imvoWZ&M z53xeca~UK~z=DkhDChtG3-&)!%>Hd20{9m>ia1&QDb7%#JZg_4jLK6n)%ybpXz*J( zsOV>SfkeD;JwbeCaM4f!LPmi`S1T<;b2)OhM6Fa6u}5g$@3MLVrFJm}0yQRsVGKjI z`5Zhf*u%9Vw^pD?0tMoLV0HXZ2YPA+so(z?E*R^m%nz>ieuVrC``Xey0y!4#d!;0bhj@t zuEIkhy;9y3;h+A^Y|mL@q@h2$oxfaS1*zGNTV@%yELnNUipi|qY7QsJswu>ah1JeW zu4LRxRC(04Si^eI>PsH~Df3idNR=j>qz$B5N zwQs+Hs*E6ha+Kk$^uZ&kGqaE=8gG^zKvAx9p(ElklkW`h7nZU!mWAuQ_`yM(rbw?p zJ6tA1eq2bq7_@_GdHzb(E{<;vc2>Y5SyXI2eH58Ru%1_n<*`enF?VnB;O>*KqtU_B z=T#~=n4UeRW%Agu-}_b+FTYfpr7a7bhY0VD5LW$z1vn>jRC>A|cICOdeWsiTNz7OR zAyQQ2m&JbL%5^o}Xst0egbm=j&$?jUmnnXvV^H}ze58lQz@UptM(>tO0Wi?L?15YT^z4=bufjQ@3l8J0U! z>QoZ1ql2`y3UUkI{{gXf0R{6~C3CIIEz!~L$AajZut6Mh6|nt{XBJ^%qUC_))w}cH zh4vst>hj-0+SZ&*x0%8rc=~Hj?@6ou%HAE_!_D>k+Y43P z5_?y?w}U%`8_o$K(3G26FtbbJQZ~J-rrb>lGY;)IExA2h7&8t>r&Hh#wTkZWZ61r$ zcr7Hf`>$DY9VAv!!ZMd%f-|6@3+zyu4%^PC(?7nvmhX5GJo4J=>}39;uj1}q%)(S{ zRk{zDF0}pHc6p=Y`YDV2HE5JC5@Ge+1IBv$7u1>b{g1jlo>!RPJEeeT$=wE^tegF% ztp6RC`$tCR-^#}SpdEk<{@)KFL3=vo!0Wbi(=dK$6%Dy+Wx3W;Vph4=^T}YJn@zxa z;8kAcKW2hcXJ>D7__E(X-q@rkPAkKS^+*R&SPsUXe!0-sy}m#ER;X)#=nc+C(4h@A zR)43`X#H8V=@EE6|Hsi5?8A92FL(u6f>M6W5qp+B0-)C5i*CB3_bMc1ZjEIJu+XY9_P5YR}R4$%bW@nBtMt zXZ0mYj9-{RxhW)1UuBB-`eY(i0j_zfXitl{q-)XrudE$H8LOP*G-#F6F!y>HIc?dj zn-dFHO_ELvJw^|3_Qotk5Z`vo1ImmgUtqq5CCHwR^vqsB`0I*JnZcg7lm)okNNK^ zHTsrb&mlo{umBzuJ8tJ!`2}0y8Gf({ohs!UejL3YMdA+jnP0++SXpMw@SL^j?tmgm zo;;4Snfoa=AIXuMBBz1}KnqKnt%zhTNoJ@Jg@h`~9L8x_5H9Svk3m|)po)D17O@@# z^a_~8NHY2~-z)L`;Bp#xHJ3ajD%kMqNV2(~U(HV`m^GN#N+<}*eti>rfQA-v*6DIV zt$og2*6^rOLId2=U@2OfREH2Y_O2aE4frQ=sFcTD@`lWlCDgivXLxEUO~^!h zjo`OUZg)tmGZxm&2rN<*(k1<^@%Jdd4#szj)sk#@P?hQL1pfS5@;=66N28;rzM|^! zVxirT-_}+xUmFtxHsJAs_xb0XR7YU4yK%nYH&-q(e1Y(7iCa~{7sZ>1aelEpgK{7c zZDL+q^WY|QzAw7=#5a4tNG!@nv6G7XeJsSLDum|Mf)z&fl%@LE&yZ zb6UpwXDDx!rZVbN0sgMbdJm|KAtbP_yd;q%%!7eWGj^|Nq|HpQCCOXRGHeUsMEpnK zV=(ZjM7kO2VSY`dJ~h;1_$Jo1IG$W4=hxD9*AU02Rig6i0H+Wsh&_$dlx(x3aw>Vr z12v@)v6=dU$XhyV^o@zS3Jy&sDh8f(uQ_F{R_)cmMvtO2b}=Ui$JUf?D`dMAwnOUL z!Z?||otg%1H*^f?z1OK8tsh253tn*vj34T?U^1t>MkL_vt92}8KBh1oTy>rKb;6#5 z?06@nHNrdtflWh4@WVHpZn>uQ>u{P@z3A%vfwmiIqbSWi6UZMd{ZRA6Gvq+>;J0uw z7`IsctqF&NHbj-SIM@Uc6YsD-Ay?-%=GJtiyr-*69tY_x2kUEp7}v>(eDOy;FN*0?xE-v@ z7>C8zThfK5LXD#&tLcd@#nOx%5b#O^5T3~`D|e{Qi@j`IzyQo)=IZ(Z-Y|l7%>jdw;^4HQ8vANg0gXOPS?>xbUyQq^D}GZz(FNAZAgLW`MN*~v zR_e7FbX^Z318W(t*L8L;-i$TN*HnPaJDJk>ex@qBKg{F94QS}{bw+XPK31=1x9j+Z zop$T9jcSIYvXB#~t*U2Z0LCfS{y-Ly??DmWw_H*;nQfxpcq);{QIPjmmhbQ$N}s=w z=t`uQ^1C_HhP*cP2*I{UVwzEokVQDs2Bioxk=C^s9_csc3iRd&i6)C1DEmjT!JRDX zsd3ugl|I?#VQ5+!>|DiNll#ACu^6az!qzw)X82mjY&)`*^oJ_Gt3LMq6#10q3_g@l#8-y?yrcTvcM0y(BY z*LUFIH)VDE7NS+p;DW~hPMW08UM4q~|NPa;mfMrP5*zHl~OkKy*)yXN>ck_0OemW`P$*`hnDM=QR z@$#;hF)f>Z-2DP|NPryigpe3Z082~4`1sl>HpnP*y>WRgovZk%U0p6qJuNnw5G~VV z90?b!Z-jF8TvU3(!9F|#j_1T|Q+^IljqM_AXu2|eVSvqXKQo`$l@N(j_R435V`mGR zCKJ0E>Sd)z?Q73$Msp9*37E3<_b#sM&8;;*#T24>?pUH)O)gsVjqwYI9RB|KQBe0^ zC%nAn=2^C>4p;VE7TRJA^4+MRwXjl*`N93mw{lL@%rVpH0cq`s6LQ7(U@2*WsxG!O?o9@6Hq2P9Cc#Dp5a zoy=w|>q$4qaj>$r4IW&x#K8*rT#e{qc2bAgjKZAWqT0GjlXb7-5Nc&zWpix{xb#5K zH})K1K&_z%i33>^j08NvMU-; z1%sUMJFZ_5U6ej| zP6z4MrzfOm3rr`N_MF=5F(_-tQ&jyWHjGD0LOGCb_ZWfTT{LmI7^VVZ-)~ZK*LbgXj zXDlEbz9)DM()8$OOr++m`#lGaS2pyiNXCDS(@cABc84AnIKdKXYksO7X$p`IX7^oz zg|KL+`S7Ih$(hQzkCtKozR3_+>X}awN+f^hk4f%4%>NuHiQ6V`%8@SdGK^a$Kk;1l z1tgEdN)HqQ4wcb-Gm6!vfXaZ`*8}|AaQr6-H|r%N83>?zB}2>Zv@Az?7@2GV^L4o% z4RZ?}YhK_7v9twFx9Pw!giIBqS;{w+piF_qo@ybv+X)?ZgE7smk#xn(`oigR#lCpV zptlnq7^Ta|Y=~p3eA{v@ZCd_-K20MNC@u`cyWl~y4}-&+Em3-Xe>&=7lbSg&=kw|T zXAA=2fD95k)&)+*1@95p$Xg=x2zVa{nEW{1&@Fxj;)F4R^FbQ4)2fJ6Xay2}4EIcb z6&GiuSjJ>LSPn@^`ONAdWvN)m)DijENKNBF_i`mbA388MK$`lKR9M{6PfjX_N-$NB z4+-GTYEsQC0NneSmG_uJ`HtG3rIIiq7g#3-##{uFQWMj+p-pTUGvtW6el&su>xjas z&BTO*_)A{6rKydn=|{sS zOAEwuj8Y0Ak4;n*l7(j>Oel*4o{r0_{a4Cq&vc|-K)lSK$)w|0zI^dIgt*O1{k5h| zs9U`Ya)5j8=8@?!vGj)^Vs(HPC=zV|QV?+;9ua!M4_iL3kWKo;faZnZ7b0#*{gfOA z-LV9Tw7>({@}HAg>?fZALipv_(vd{XxeHF6BZOq8qpIE}7x$5$fwH8Omi8&Q8 z0=%6SE3h-dGo@5oEcK}c=ad#gs?Mw{Dwe4gsx`$n_x$rJ2=fU zM&;SJqMM-P9LMQ4A`=;jY5=871P(aIvXaTiUrgMRvN=k{>=EqV1N0 zF;|!XU&&1>I4{e042=P!!rJ!aDdzeU#gDoaSeAo8r$T??;1$n>WmO4QPA<&LM%qof zrAP7Sb3CLLxJn$<$&X*tE%Vsu2Slwt(5+G5rBoUe^IfkNPS?>;ZUrJIixLYFlL+I^ zi+p%N5lPc@LUxTgQ%J|fAQU#b;nM?XNR1y5g)p6efg=_e#Z*f!%W_g9D;xHh+J;7E-1_HH-b&~FZwCL zO&fS>!WCFE>ZVBAQ&iR&rJTZn`SANln`)Sj9Nr4~@PCMW1kX5J*f_HxYdl3eWUlhq z0x)K)>xUJ-58&zug2Cu+_PlC4$j*3i%;2);&Ct7aN4xe%*^^U#5L|?p8dmXC0hCJIEYfggG zX-?t~=S4kKOR8B~c}b|3o$Al)xwP(El|F2X9ACSz(i>ju<|`{5%IS>e_Bw5wZgHoZ z3+LqR2F>~WM5a{k_XQYxv7oo?7Tl=L-6}|ejttJg_%@9PX>9Lyy=CW(RWCdAj65Uf zIm?P@p(KpotQwBcoF(8(Fiw}1!excPte2$A6vCG{A>e9lyJ4NDD__O#W9;bR=T zO)@dRShLPp(^gmCGf2^Y_p#;ZT{UmKGMgHZ7V%mP(~!R1hWp%$$JUFMfn_B*$wmAo z#YvBq?{m(nuxP-I(mCA7x3#hNk25#27f0g%6$HaTKjqyc7)B(2y)RD4v*lHOzn zlZWq${w!{qAVlR75i`Bbgj{ek$S5&0z%-`HnG;rvtQI7XnmSKL6)zlBj@l|nnKNY? z06T`*iJfV5Ryz_m$Ce?Sp!*YpD8*BZoGIqj>AM7R?zOQIzQYFyi9bBk!xvPc zV6u_oQgMs*Z%u`MGa;tPH>ngtP%!!%r<3eev2^VMVUB)|sr5(}ibvhmPn1^p;mtA= ziI4za=GzwEsvc0d?P6>tO8IzsCkB{sTgJk3a!?V1S!Ox6!H|CJf*~-LVgaE-K`<#!qRO7TI3{kU@T9BH_B}J@ed`6WrC6`Khf|G-!E#`C7FndPeuRhIQlSz8w3x}2Y zveiOlG8-t{NTXakvd{3onP*}2&4z2dJ<9agg*GdJVY^~@H1!Si2t_EvG>s*Tre8w) z=2~!DV@0fF1b@3wDwN@}M39DgJm*wmv03l%&mw#kXKn6eaPVuq#R}paloPcSHP*oZ z*G>a=Kd-Vw@GgwhtzIrUaD$06n^}YLCN6`IkXghVswoEvOe_1P zv?|Z7t_hkU^9VcJ?TGz>k{Gy5B`^f3I6m|@V*_rR`peKvir1W+Oe{&1pnT_y%KlJq>}DLPoc zcsYNPc{zFbk@e}g*f?5{3J%RB-sWD2#J_D3I?;pbka~og8n#5mqiW_0a~2C@oPNPx zOXqIosl6#JW~{8$LZU1dYR9T^NE3H>X-nh6j!e=IEAP{gUcDTir{#Mr3v1h1_I$gq zK7A121j_^bkGvN!8yr?H0W=S5>xYh%fp^5E>`-NCKwjHJ+p)#WUPPMk4_-6S3I5?v zezUkQ9j^Q8MX(FE_c-r~IE|*b5qoyDT=t}o*;tdLZ;Ylx417YsFBCjX@c?gFgES(` zP_Xyshz&>6$*28%A-Whth4djgH*2a8fivM?Oy~o~N!EN(%#|}1t*KF@l-{*YB;_=0 z)^fAv9}!^?*v@)p#Rf_GCsl)7@5sTDk->ASKkyfDVCOzE;MKq38(l2wMrmYleq&@y z1IL4NHJl0Tr zimnKnW>qVfzU&Xpj#cEPAEHuh;jTnhw)1o$PO9`2uzKOz4!1Ze(ZD{LrF9c^OE>V% z#A*IQWDztD@wG2Dx$VBf%rMG~q+K36tsW;Wk9PZ>uY!~>V+#6T64N-spx#1&FirW7 znBsq95dQDu9L3Q;1V2zHzF{Lndrh&!fu%OfDNbKolqdniND64@I|iZ_ULi(karG%u zdZNHYhw#%resT`c)sJj8Ov{I}4y0Ri;C8rZsoH(HsM`1xUlY@h6%QUAm@}5kyb#|~ z`Sy5sV~#G#n9vf_T^p6gIRcv^0nn5#(?q~Z?vJi|)FEu4aA|I1z??pbIv9cjuTWdo z7gO;}lL;>M_HW~AKl@`u6LPr@L}83C^=>nl*a|aAyGUu@OZFg!bX5f1cWxl(e&Tr6 zGMo%Y=aEn=9kk{2P6+@~%U3!I-u2 zIbqd~4g3Q?{7Ex_N9zJ4DPa40bANq<*4DnPr}j7z$%I+tW^)cMnjYM?X6pno7A+Sp zBjPdN!)CdaX!=5d6dC^PNsO1Y%51wFg_Z}}Ir_My{A*@Sn1P`_RhTwmBIFVW6P*!CU#ufvXbFcV5xp#qMArq4%n3Ss zBv!1#IeHE<9cETE;8{3BE}RL=P(r_IpoTJA(-5exS@eNGLfRIEu6x*IDi-nig4iZwb2GYtfxA0!4ik-|vbHG2bH*4G49t|0oLb z-x%lr7V7^dldSY7VOCrFv>Xm|chmCJE>7jUpp;k$o<2!Bp&BD$UXfDC!1ULp>UHIM zLwdKdhg~8iVKi!lr|++Vo!_&>3QV0ldIl7*luO!*irQp1Og^42s=9#6vzemAJ$Ztl z$^lPlL+oKns4d3rZx8Dg-^vYzVphVG&|2bpP0%+?uJD8^kF91;#+UKHRSc7h!M!P4 z7;sisP+-j`?y>@5>trTR;tMXn77eq*e2+gpF4ZfKe+G8VOcagUk16X z+0$H-*$zir;r6Ik$W@P}p0w5W=%I$ef!ATzQ=P`6)Hg3|@+4ij8fYT5#__eruOvKx zT!b<1;^hD@1TqkO6^-n!)+(N|brpb~H*2*n?OfVd0Ag!nR-#4aQw$n!rka6>bDvw2 zwQ^wXso~72#k_!2zjMW$Uv4E}&BiTBhxG20!(~gjLs}S27p9l``Tw!@j?tCx+qURR z1r^)2ZB}gCwry3Vk{R2!ZQH2Www+XrJJ;HK-+T7C=dJTjd+%F5rnUbZ{WnG*qxX&u zosW~`hfg;#%gSc!q`IF0rEkwq!y0i-`}4pnPR0zmw7SImOrk=)cq~40;chZv{=&h2 zxUe>f1OCJ+akEwoKJ~CP`Hc*^c|dh#RnZB2Tfv45_lB#U_42W1v%V&J!=ryBj(m$MECA2U zi9?r^vOGbFgQcU3HL>Aa(I$r9uf^Fh6aLxvQ0ep6meM`im-jf`7O@FA~^1NwL(Vw|V0Qd2iB#el5GAf26PWDW#fGi|vRt+^?)(|NnuXGSuKplgIyz)Cj z=GwlThFSW+dE}SuUU7a11ZHEgp|8W{S#Rh*FaRJc!9ML4?&fp+oVn>edG*$MMC$qS z224-T=|Mo@j3{*M2$Gia$r~t^Qc2KY|L}Y|zX$u)ABLQq_@oo63S>mo5?j)S*-(~b zeR^Z6y~-O`i@Xzmw8F+O$nhj}&~bQ7W5BkWc3+I6LF8+!8jPs$$Z}`ia>L=-)Ekc_My$l&a`RYnZxY44O@hp9Q;*jPJB&Qe>2oM=S?jp0veW19%Mb$4RZvdautnD?lgDjZFD(>!LIPey?Zlbo? zVoUZLvi{tnU zW5X$LE-dIvl$HaN^$yL1=93l_!W_Dt!Ri19`r>tEQ2NuP5S%N_Sx$r5qpu0f)?QN3 zT8nTwqs~zM&Um>;nWtT7c;Ouemei-0KNk99eIb}ziafKyA|JNMdr!MQaa%<}^kNJd_91H04ut}>v|fFS zt(0-manYJ)0h>6L_opxUoVCnyaN!&O`Dk!%>9T9+@B=x0t47XMLH~WH=vf4n3<`ek z=a2!hICmuXX!FTFDiO)Vs{ePsMfhO}tr;S&spI-DQStRoPeF|0oYlDJw|^Kv?EV4p zHTxc+I|3V08iPmps1dBJLlf&eQP_Zzvha4t8Ol|sw90Dq<3NmY|~6M?5*bp*hPE2>+hhAM8jdx1$;qrK{%?!0`bnOe?;ZTg@6C{9Mac;`(&)Mz7QkaUC0W?06dib3YnU!8y^8I^rOEm z^#7K&_>YDDe;)QqwGC(NKa0Jmj?}KANK$Kdl|52SD2}+2K*dN#s$6#i8!cdwq2mYL zh6K6^L*wsX{zF*i88ee3%Qw9@E8?Go!_HUq;Y6CNA_SaA4p&^=cb-${+5VpoJl|<7 zWDKB}hHhxX;&hVt0i^blw@suR#{6#yA9i2JH)Hp8C21w?PCVpq=_EJhcR}$8PAQp4 z!xzm;RoJTk9L4IEo%nin6?cAB{mNm(d!&groV0Sq)->d}5DUIC6=l(GqIQ#Q;06cW z#LYRh9=ib<6D3F6PdIy?6|Hf!4szbv+7Wrf%pPRxsIw4PfQDBo@Ytj26pY8T(W*mp z2QT=F^`ef{h2oMMRr`VPI_tu-!%%uVZo98)>lPsa10eU3Be}A=e)FYK$faqxl}R>L zvJP8h(BXsWvJ`q@RhOQZ%7w2zKQIUF#!8#=a7CYG;*w$Fr&~AYb3Ut(Cqtb0LO_1H zopQw3eYLUr1Ct^iN5Ln1l;06Kb`vFg>rtvJThhH;oQuixJ7>Bc_HRyhF&+M$cnPoE z`lH#IE|Zc~J|GHSyFLo`K@f%1KR_gjin`f%ImD8;1Dk9^hUr}uY z7MpNI2cIe+*{@fx*zQdG%_y32DmDjSSHoV*x*K>&5H}PA`s!0ou>@l?9kc0&?o2>} zQCC^SM((j=7L}?(SB!3mE%MW0ru1WhuC-X?1Sms%i!)J+i)OOjW-oH}G1^x4G23?b zG0X{LfO9xkXcp@N2W6XwTW;yEncmV!?%45d?5V!xNxpl=?LUo0$Lk-X^o2IbhUy=! ziPJW$FNxV%oejt=0H>iULRXZz$aU^#d%zJyTSu3RpC|7=;q0T=*5+wv1(dCQUE9@1 z&pWD!-i>wwlkz7L&m%<(J5j=Pwq((oA}sv|xx)SOUF--m?@{^Lf4aXjU)m*c_oV+v zvdqLBML(*qb_7vSdOVGRZMypl|0odQZalHmzaMFkj=QiQU%K?2qw(z&MO4KlHX*_X_o8ogFA z=RPquHq_613T3s73(_MHrSMjFFb&xxR(r3Wp_5K6xR`c`$%}RHNdcEZ!rx$;)iSIRU9tG2lqR)1`O_{lB$X1UFt!iIO9i8!Ff|cb`l;!poji6^Zt3DP3P3_ANt+8 z&(ro!0V?7vK(xV1o2|0v!#~xtrkOl<4yd#A(F*WWqe9)=5on#n9h^cc|WS zLnFmd2zr8RxYBm^}9h^0*W^{Bg%D;d)dgUXdHz@vB=CK zGu^ef+8j}+$PN=)3I}{B?8JfRCE$Qb7c-)C-DV8n=3_dZ9*u7_+$pkMwHGnfIV>u} zoYClp4WQ7LP`uD3Lp8j?rswzspixWA;d}18!9-3~DvU|!Lkoi5lDVymUq{UvhUt^8 zGOLS|j@5@pSAtzxw(~xTTSyxjRU&hZ!CO;vb*o%*W{zm!Aon|K#nADBR^d}5LeyfA zuH?N5Gj!mXyKu_1eMLU}u<5ZfGwg?vC_bdRnDF;5E}Z}c?mmoG8)?o^!*tYWR6c_r zd_Q@9{8aOwo_!a-podI#Ydu==^d(quE%Xg;pVW42PS`TTj55``UE)1Sh@(-}tQxDR zfb2?DxRNHWy>n9Y%(Q<0a^a4$Dh zd?ZC<$QXsjqmNN7JJ!;ENkwi#FT2yr*v$B6@5`g6S<32E3Vi)SSNBX}qS#)jjk8mK zuMObm4C}oKwily(Bm2Y^b>m*Diw^Udr?O)~i9VyR%LM_2ErV=vlo7^BgqV?=H&47m zS1Rq@=c7q?v5Jc5k|^v29;cmgM%GjCNu_}Bcamc=-vmEb!Z1j~HbTV`Q zh}RN42#K^0VhBf_>feST8HxSyCy*yh2L`f>(}rYLj1{HWLW=?=VzCf?Y$D%IU8om3 z>xEHFv9KQP6hTrFGzVsd9BsW}Q}sDunN7MCG;hq1Hv;HFp_cyjMD3pboO31V;% zMzDNm9_i*IAR1vpz~{5#)QI*LDO_ay1Su>@rgH^?CEa7AC^ycRa;}!QF^i)`z2gIg z3sCMe`&?pNkQYsMVcJv?Kn>;vNAkdeRXBX{EDvwJV=eZj6siiy6MIB;@^HJ& zJ7xS~wb~8+Eau?e{t{Vnz|q$J8K@@nx4puDclr76b^2$s@XuncRFigJQ$hT&Bh{2s zfV9MEJ|ST(po@x|PnowR>LqoBK`Q}sFrti(&tL6Bql~Mqk{-7=n#uZ!z(=c5w>?~A zJZ^ZINE<)EPFiz4VR4yhpN0M0%r&gV9$LVt6sE{Jl*g|d9{Qh$-n|@1{hB;B z95Cy@H_X;nXdJq0Y?s!#5!S3>_f)H~I6LOZf8I`qpS{XwB&nAKgSema=JBvUYto=- zm~FuD>X|9rhOvh!VoZ>%Km<;AZZQv9+ptiYqf>s&GjHkKizmBS+x>7TpvVg@9RveF^fm@U08& zUiK@gP8xgWTZgO?rn@sN6x52m=;;D4}}G~(M8&iIZk5(m||ET z$`SE^4AgyN8JrTJ?yBhr-aM>NfIQs#usH-!^$DBoUA}AO;^9tL(Z*5bt6HXSPKoPy z!Bi1ZmvgdZKj6SjH>C5>XmcP%$|d$?}%nE7L#`si=$`J3vxIpc(run>9;jPhIiR4*RUhUg52 z*!QaO@|q<^%*UQ$-;N~u2i_1f$$out6w_hx|Kct7#Z+oOB#|@tPRSh`gOv3;p+)X3 zjFB<94U;9gU11`TUd37M0(mxKQFJI{)Fl@<0)p=XEl31WXER`>B^csa$qU0tsg!^& zz=^Xo2~p&Q;Bm|?B6X=5zjmC$9LWYf*ak;tIyrB6H?&FHIWL|`Z4X2qfV?|MDMQkvbK!<*~iib=b*DD!HSDwls0~-0v*m zidPgRqR{3xCtO3JG*Id&Rb;%vJ^?NC9^Ji;e+;sIC$7pBF<+8Y3*E}ulI@7)v{6zO zk(551y>;#&Ov{3ll2`B!FpHxQmJyiv-8C1^ByrjxBDa;Wl2ik5b$W1rw9oY^(T@Kb zzIBEUbfE^$94g4|qRs#O?*DfxkAJuGNL01{$EH+h+!%=p;h?Z2q)@bc z8x%~CmU@AjNwh1jF*9MIQ2Ng_8ZiDR>9?FvkzL?oC)9t`!`!XC(zLmkH8XWH)%%j` zaiXC6G8;+I!w^T zI`Af!>E5I=jO0FSdG;5zxy9N{CLTcKqq3{oT&7cL5;f3PWh^}56f*xQY7M?$VzX`> zt5;U9>GwtiBTM=-ogQkE>=8GNSZiK*nXHF&?$>m;d%mAdYFnJvYPD_SG)9i8IY!cA zgNWm>GyC`Gwc}c3^edX88|$;S!v4m$vRu`Kpp?y6x5u->_G3cpb1TDxFX!;x>YdzX6ppz>bYbGRL0{dO&`b`oQ_ zdT=WsK-vS2AE3eNfbAd602(FW}JQpF96W`h+pcs2GcJ3N>if#WU0N?4t zalK^Ygy!yrmm%rU42xX;E&;Quky}h|UjE_rl>H5YS;s=B-Ysz~KQ70*3 z7IRk6o@V}+R+G1G0!ywt9j<7;YN?zd_wr^B#c|={>Cxw>Ss=;~*$6)fVVI%CF-Skw z3z{H~?})Ef^C*nKjUO68&Yg@NJoc30KJ>4M!-skIn(77vPWk*p2q+hkK?o+PQRZ9$ z=Y&JO)EFrOqPR3Jq5E>7FEKd3pNAO>pXNlxXRU>dYb`XG z)aO|Tl_b1F5pcrUS?NkjSSvTFv9PXfGjbPP$prK0JxY9)ykC|JMV%`&RkQVT5U|z@ zI<-#$j1#l&@XdTZY*w`{OtHiXE%%&!&Jpm@%+}XjJ0#?gH&JIo|b)4HATieD6`3vK>pMQp~`TVgpgn?-6&#Qq-$`~(W)&=YN>fs4q6CH`m ztkJmn`#mXA3Ef(=0>x&@`uRijH9^2us5h~8mUdAWeZ#45y*4E18R&tD+slj|6 z#VhS;ok)$|0FS=G<$a+-U>imJ^bGH=VIKg#9ceO#W>BaqBkIU{ru#6IJNZQh!idmg znsi^NY9oYd0bYzDmAEMhsDj5umRd-2AG}OKspWJTvSNw$+=pD7Tk*DXb23%n=jAkt zgy?-Xr|O&34^@+w6{aMy{f16xV+60+-^5ioQ8%&Kz}jMv5M~GrV)TXJi~QpG^SL(U z3%d9!auUUlTT%lwWd#27#b#gdL9F)QPvLmke*&sJFRTR@Pz4VM+|&F&tKk1jiGzcM zy^H-nEsYia`uktbp3?HboRtrB%0dPGk`PF0WRxDzuME_I79DXC#10J00H$YgZ5A{8$vE8BldtG1QX8^M_~qZMj7Iu3+EZ7 zTm)2}TCJ(zLpSM_sA5}B|LNblhl7Au~WS*mi0*2-`SZ7InT z^Jrt|%l(8GsVQ(}YoFMjI>d(kVZ9e6B1$7BJ%bZgN+qjXc_RR%PLNh^NDHjJL^20u zn^fDmc_U5OIj(raoL9r;1?&7ml~z`G*R!unj(u;DaX6mlUzs%1;A$kGh7n><+PtYS zJ{b>mrAKZZ!p3Q^`}VP!O)5qpg%XeeBQekY8%zTfQ1)q$B3Kf0Q}oj?*NWsSF-k|_ z`~lY^?tyMha&nG|)L3H?bus>+IMV@!k$LCnwNY@z=!4{N(Fh$qkG#e`2djCj>_+{) z(qrGodf!HAd_c{=W=FIx?2zhTaC+_GQ&&r5{nXVWD;m3*O0E}g+;`#nFg+0A<)YgD z%T&-6dHH8D@N4`2+w%E8iJ>xhkt!=aPPj+N;0+6CqB5^c!aOyW$W_`a{e}}mMUt`rP_p2 z^k?qXE??D5BOY1*Z(?05$@12Y32D(US+1Qe+i_Ou%mWxuL(Msds$C=7kr9`StYr7J zAZ`Ws5fv9BqN4vtC5hp1dm%21pZByt$)**om}XM0}w8s|_V^qGHvEOa`3E!8rA(D{0XcNJvyom3d#S zly&C)yLxXD%V3<`(t0I~*>*3F{HF)im{m2E`6+3<9s+YY;<#g?_fyJ-*`+!Zb}jW( z_JyV0`x}DRU9V!ut%tD+dsMyDfL`(z@||KmFDb*b|iE0bINmAo=IjmVUHuz&Z%!hn;@1W(m_b$Vd6)PN_1Z&M_@I;Aev~%NLUXdJ*E_0HpJ_uae$pcTOMDzS{ z8N6n~&T?Xl^zm6|f{TLA$(b1UubAIAAme2FMXV7##VpJlIK;aHGUEgpBGg8m@(|vO z7y?0{(LpR%I~$rqzaKAS@y^_^c*x7TAaUE^t>yM%lj2V_*dd`H05ZV(=4A21*BGDs zT5MTh)+RdeWV5Y2Jn{5MTAWK`7_NQX==SL^S3pn_W`x_PX!Df3MsB&^d|T{q%|i_4 z?Sx#JtF@WSODLa*8125&t5Q^2X>}F|HAs~Q7O%zLfM}wGtduyjTeX+?p0DSLwpTZm zxlsu6kTZ#z&m9I%_vc;-^VWeZl@1CGD!ACZ;eHT6LS_bS7|offY6auYBj$c%KLK2W z)d#9Y@&`XluR>V2t1qH=|K;9R>EvHz4tztU|Mpm({J(OO`S;5G7tN&tEM1^wn_P24 zgI#4|12m=6jgpp$>b@Bilp1~-Yj43chcNs_5v`_M`i3(2=TH8A4g%625--33 zH}BYStLuXa7qhM_9~Serqi(-9FvjQwP9w?z#t=ks6#HF zY+&ex4lu@*pWh+qu-Mqv(`nf%qD4(2 z@OljV_TgH!wmuGs>~GsdhNx6Rt;URldq@8C$?mrKox9{B`bv2(yt9$fiY~&V_!y2k z^wB5KPH)qPsTOa@$*~uox^89V*HA+IV~rX;MzJ`~4c(_7lTy+8moo%lt~GS9@zBTwc*4gUk=I3d zZ}u=g=7}9**#%B_6fj*t;{esLnc^@tILCG3^4V&{zK|ICDFO31;yDwnl^<{JHO2@I z!Ey@-#6d(1Ndjp-$k$mFLhMu^&wW%v&nwE5!}#ria!)7lDqj5~9xjq5qcA)4&zg09 zmwF5wLbZy~<`G7ZH#zx^b@_@|(EgnT3MW*!!~IcxXmxSCiTr{k4q}y=0e>{Bur!Z5vYCFIwk#uY@1mFM#smK^2 zBoH~&SN2Esd|ZWLC0Y8I9O4@}K>@VX*jHd4L*04mh7GBis*i-bdC}|r$vEkjzt1~_ z0mw5wrN;RHTS1Tq5oxan#N3jn#h1$j7HdgC1UrLWSsr_a>agQxX{9e9b zeTyjw^AIm_$j`RIg%Rsx=DT>SpYL_%Ph(f$wns}3beb||s;Eu}%Xmg4!aOiut$DXz zk(BeF)6w0rPx=sfsk)|-=5PyjPGnr>TER7(bv(FDI&;}0P@g@qog^Dzxj-$6qBe&# z1#GOyN2gaokqb+s>d^u>!*yB_{wipF0UgBjVz7n{K1L(;k|lz$$B;v3LE|<9>y2}w znML}nWooP2gy~^#lVAgv=vpYz!&k7f>~z$8^)Xn`P)VE#1)75d-!w&_*&#T`biM(c zBKTPdAV#(3hb;KijZm-jK59pwwUnv-nN8y};_lr^>-}5%J#qg4en)?*6-)q905TLy?SI%B00H>FbGh1}h1k zgdYeG*-!MK0wV$=MB&>C3=Sv}FwBDYg0CV1%x+Op`}bTvqp8kM@}|^C*$uuK9)@tz z%za^mQhI%1u?PKhb-@|&kaq3vBrY7(NW+v)xbC=Pd+?_E`rFDdhS}1bmu(z0yu#wv z0`-0EDGp!EsPeZF6BgLKjhtcI*uWj-7ezj?B{(-UosCF{SLVSDGkV3jMOGPHXmkdG z!VTh9hZsfwl%e>Vkz#?o0j%y~qsXAhXxqNA`Tmfb@f^P9$Q`y($KV#5rqZ;Bbxxre z(U0fN@!wsXbn|^UruF-alKD|X`pWhvCG)pZ6rTU88l6lXU4cQWf6fs93<(xAHnw-Q za{;<)*_oRHJ59TPUb7df+NuMMwfq32Hjpr)df|m^A*#p{Lg91@h;US^%h$vps9O$_ zLp7}DRqg3g?{FU=pFatXxxVl{;Oa*a@D6ux>)&{E)VJFRX?Q8{pRcF#uuQQ$wz`{r zKD`cS{~^-{iPc^B8DaRxGyDCX?+h=hED?Z=R$3#}hz$aYKtdy4AtD#=nQ}2`Bg7De zVCWjDY2ezpb`NF72Yb+*sqaaruPKlLGp|*sNn?F-%iO>d5S3A-*_5}EF4Z`MwHm3b zyEoqWj%Ua0g7J_rELBuyktj36u3E_~t(`%})j2^xxkxXZ&Tkf?+LBpLos`JH5-5f; zfaqr42ID1JYDZmEcCPZ=W??b8sY(YZ4X{&VX0N^tD?K+y75OarOu(n30>q<1K?iksDC`0%m0U<6$y^&1b2n#1 z6bxOoWY1o{#1Vr<^SXsso}so>xhA_#l5te!<+_?J@0c44GVmK>R+t+Vnk}KBbiDT} z!>VJ)o%YFmyapl*&M$5-VnKg46HgwN92f|(HXpa4Erb;~r zFfHfdCSfak0}|!wEis5fE4PiNc^RE zy@8v7RP|TnA-G*-j08@TRv`@KT#e4wLK!B+5+oOzOZQ|eZ((Uj+o^J~#?0K;^g6#o zH6A%fwltl~%3oAzRi*bi3^cF1$bPnE?D`BD=%33$)sEAR_kBDY8IWCEAoB2llPPuq z{wYQX$HZ^cLfDNlaN(ww~0An7DOSFMfyrLOKO)>WQUtJXa*VK)5XMK=*&ORJB zZOuN3IsGQy@e~%4QFufNxcow#soX;QrQQ4|RW}den>ZrBGBzoHYh~yKcoE$yEyRlF z_)EVIOr9@4u{6*Z`-u-bOK5M*pftQ^yr9)yAv3!=6IxgH_im zP&YDYVh=y-P8FPY(@ut?`wo^tGdo1*szt%bv(UnojD5Y#X6DJ5($2=C9ILRNASv1KQ1p!Z z@&Lp08!|edAJkRWp8r(o$=846MR{)1Mb!WYZMwhRQS z4hJx4)U#xDvvSAOh$K>Oq*J#@TArPNr*IuUh$_68b1df&%UMuK9%y zAK0B}-V0LH(P^)bl|4bOL4Q-bf%>L;of)9mHn$qUC6q*Rdgh% z#C;D-v4=Hs!a&_MB9YhccgbEceJVu)My|LB<*aDE&3KNw<3jnjyF_EiE=7L)-##Kn z=K6g(9mYpG_k5uqAQp#!lWMTz{1v=jQN)!^48AF+w3CdyJG2goGwFNRNH;NWn-E`d z97Xx^)9`ENx~vEYF2-&_1>c6^;pl0OtWS{IE8%J+eH4$;IZE(^YgWmE_>V0G?Dq!s zTNL@}ZmlP{)Z!3(1hS}{H=QT6QTtfhw}5>TD&MgA2c%hH&|n3a;1S$iE^fTt4AzIT zIKyGz9!4AR>b*8~0dV7ySk*74bz{ncD0=U{{ze*thJT2@oKwtkzDuRRTH(S`hDP&? zohu?x-NYo6#v~XCND;@yh@@x+9N$5u4#^0NsBEXl_1?-eE}vyVZz!uQ;}r0u{+6A9 z5HMXe#z)NM^{0-msOALUXGws@SFrOuI*o>;QzrR zT6C(yriHXH1|S}R(XmRvF<4psLH2zYM=y@&WYLu4a#Q8hej4a*a}AoQ_)k2tcfy_8 z*svkg&6v3nzTEJh;+o<*;yPM?yIGRw2Tc*=Q~Pl9C5)8TkTC>np7TI4g_D+~Ek+s+ z1E|8;7smD(A={U~XR=ba#1FNXlC|AtfzPOOa$7%> zw4sCAoIgVhfGI9{Ff{w%`?XI*Aj_K^`r)rczfnv+IbX;+xd|rW!XCt~= zjSRXA>(ZEY1YP5<+Aag&;Sm^QdC^6q(QpCwy-8 zBr!bvB+_5Z{jEPCimUN_#~Baz6?UnW-yvw|$1sDCJn7c9ZF}cgrNg{D|)Xrjm^+Pi0m(pnB@%Gx7o@#xs)=buoN;w%3r6kY1?c$#$ZSnUhEs|K9r^YJ+b zbcL=*hw#Bxiup0so3O}LSys#86sVO&UA!1Bi2q`I&SQ6oj zkB}K74k9z&a#2G4ItlFWv|^z7(LUir@ydBk&_3B2#7^k z|36}pxT(uOfk;I~<{wy8sG|L6+Wkk=g-Do!lvSKiE=Z6z9z|2U5_Z7ccgHU&)P@i| zsXL|4G4hcm#84+GQ15U+XH^8>90s}KiTm4mEI*X~nzjL=C~qu}=@sXP^ULPfi_;FT zyS1x7stj1lyZpE-iK4;b!k>hC(a`j*Ms~^qs!?ez6>4*KwMdiMbi`Hrp}V4@%Bk%v zo-jGk=26#;6wb^lSKgXxhk%(^d#+P(WbOy-?ri~rXONoi9+>Q_uq%DEsr#pT>2|k8 z=8op+U)lZlL)03kGkt7=VaJOwT&R^e^H8ocp&u`#2(#QoDf^|yfv23-Ne8aFp5o_M zT6FbejBQ5kO*)Y?O;~abqLh&Rwf=|*BFD;Jf%U44+1KVBM&5~lGj~&Eh=3~&d3(TK*A%l<# zDx*5=Sti~nko!jk0d}so#dKc)-f&B|0pH8chuQX>^%Ek0>t;9P*($u<`n@p^G9V0P zb8u&M*d4e0Y=_3?zTCk9FN%Ugb)Co?Vf=*>?R2ye+vRYuX@hfw1na%rjNOXD&@+dpw1Xd)et91OVgM5B_8HI^2WT)IP0FHhpe`LP14!U71!AMtRK5U z`yP3dJi<2-eQtOkJ*%(a7ohcmG8QiJihSD77je*tAC>4e?TuNii$EYc49kKkiiY7BO5Gx}%4DKvtkiontdL_IC6gZwxP|;WtPgu+kS9j=tB{N? zo?TjrYVy}3qvN&_>8YO)^x@n9YQpZo<{-l`xa#I#d{3IeU9bX>?`ip4_EqqIG}Zu1 z7r559H1kw8b#bw@`&U3IkoEcBqs@kf%#YQ@WQ7X2AEdH-+EC!2g>2Y*Qh$h+w~#Km zST9GmbD+Kb#rsT7E9M%TufB|8`N!{hTwc}*=;3Dgjrj5%c}{vxuB3N=Jik+Y=Pmp3 z#K3=)huJ3s_VbA_jMxlqBFK>@jPMX`UCn_a4AQLcNnx^(@>}1qVnQupbNH;WWUmvZ zdwyU|UQV7tA`!1Ty&83TAuusx05P2fPTK;Yv4l-GIlZ;NZq%a1Ug@QF4NZWFW zy0YNpI8KlCD@lVj=;QY#GZs2#o-6$!cA9On73`0FOv8t%RJMSyfr8cVdgYgX5) zT+^8?)oZXEJU4V7w&81iNVDyg*)^skxlysgbVqS{&I!*I5eUX&HuvekEX4>LFx}zkGOGYtBaOpSb@Swq_lk)ai z4TcmLBnc>B??wb^uc|NP)22 zQOcrkwEM~)mRcfP5YY5)f?c4GzSMy$&LX8@N291160u$>~ zMd;aa0Tp-WLlS56#ssmtLDJfo&-k^)PpX|98GC%$@ zR{;^Rz~KaT7=wS?VQ~LXVM@Wt(#_ELpSf56Vx^N}Y~t)_m2#SXlO?PIl1T||6ES)P zYQ_4(!4xXO`px9pF6waHDIh-}KEHqVB6vgzMe~WDQAB@$z0=@FySpWW$K{j|FthMD zU)`<0uJgp}f4+Tu{Ud*xr2GA2+zELjMJ@-%o=mYPB5Z~8k_>4uBdl3{c%&oJc7j3CV)LthkQAG)5xrQoYHXYj7!LMCDa4AJWtHmarUfSBp32t>n zpc)!=opMEvjiF_<9DZ%GJ}p+C4y{s4mzHM;e(v$`c{4D;Br#kwzSXW)C@a;k&pNx2 z25e5V&eg1nW!ONQTt5*sGc*`bzHP_S($8P+tllzEx61(2X17G=m`%WhtZOq{1Itvn zt#-(J^Sc`5peT8&5Zaw<&_Qso-?s}sQHj2u=f(I{8wmo$-`nO4DcSpyTtHE=B)84C@bQA^X{f1uG(*v2_2 zUA8&*BGEgD;(Bc;_UQ{3LP&4J%vw$QDA<)yokkIrH3vIbWSm2^yO z!6@%ChTNG>2ff9^LY;hSgHjm<+pD%}2fC`vL%;tkW-$Qnr#|#q$#xK%^dbk{xl1G$ zy*!T~0}!FwFpfx79a&K@Y9|V2_+#5;%fA6tr~gZ-XfSj{TmR!tED+4pQA%k|?Vu;(JvMQ)z;B zmWmw+JynO-ZeAMFQ%yD-&wcm6$5lmwu62v;W~`G|a@cs6#p8OX^My>yOVv;m<3{Y} z5T>=U97`HX^xvk#H#11$Z^w#7^xUo-9kQNKI8BhEZH(hhuwD%qD@`4j**t*l;n--i z$u4|zKCt-&-FpOx21Vz`ZDt9@tMEq>$vi7e_|Ak3;BC@0pqGAJuEz*;H%Mt zc|jI&+YA;@;EVAC#Xz?Z?pz5#-0s$RwYaas1Vdvoc$Mu;q`S;_-oFlaiF{Iqrbi}W4c_PA! zVnT{eKPYRH0tWgQvWkb2G~of8`{_?7&@3b{YxhCPjis? zraC!tNYVp{-?+T>(dE{WVLMY}Ax~*PCHQ(uZ-47@E9y7SIo*(w+%>QtAAbB2q`e6GC9)w%9ZH2z%G_u_mkQaa~++@kR zdVx{-6}DoKaqE6vz)9V$l?4@+OM0ZJ|7XsExdghjN}QFK@9HQ{)@UaEqwS`%2nDW% z{N*Z0f6~r#5_u2_ZT6M5kAZcU2OH`S*oLTD6j_|26FA*xi4pU4$E@ogK(>fTxn5U$ zao9!|FxUkN=E(VgQ%e#6GD?DoWnqx0>3{N3L$OT^3_jY?9QYcIQXp5x^ot}6cI5zEII zkUMo%iciFkU9K9eQK1z*hj*u=8#^67*mJXwR#9vpuwcQpW8%$Le9#!tt=5077G7{g zD~?rHP*fa5?&prKnvp$w^j2ZyxJqMa_0%3T(r~>ZxFYEv%bpZeXKAwI`M4mpz((|x z^5aiz62?>2dhpN0!nwyp^D2ccA2EtDic*6LvwV>gD0IwX#zDmzG|B43OU^D(j8x(b z3UZ9fNKL>XKRCJlO1dcvGRyRRg2)51MLt6^ELG|^N!(YaU8RIf!oC&e$%u9n2XvZ1 ztwPT*=A#;RHNUJwj2b@?_fvzW%OHr+7Kxr3pYS@OT%{_NEfT&;;ZRq~gisgC0Y{Cm z>6EQ+D1ZKD9pBWUB!Sl#5P$3H@$G-~uqv6F1M?(JokUFCERFxQ*HAnAOZIP@Zqhso zVU;ToE`<1l)i+QaTnb4>dtN}QkxKQH#3tTaBkdBfA!~W_?w7l(J;)qwgdO-%}oJO>zxlfvGpQsg*| z4|6=CqlBJdA~_Q_T47qE<)|T0(1SkDi?ak%A!0Z|HfMA{BV1w~+!;<2n8shEi~N;?VozUYBai0Yum&FrN|B})S+n@Czl#b_|oRgnc5W^PlI z(W)2-12AW+j9JYZB-2bZOzf+e&0-bZdUob%##fXm*$t&+GMM6XS6P+03JK6>>3P?A z*H2Wi{g#DGBqYNg8>=?c-m82zro1__}-vq&kmY?dnBbxwfy7Zw6N zrz>qGH_S5b)eVio=shp@-p5k&kAR2;-NY3G3yNr)_lw4KYZ=8`sRbR&d7BiTvrRH( zf(Z}3EH=dK9ZAs7lfN*3O3QjB&epYLhl9?;Yc`fpsSW1zBfQvPnJ}mi)1qb+?ip=JT7vr-bq*e`sd5Hds~m1xS0@w^0AB%tS0e=~H{GS7*c>%z z;`1Fr)Q5Br`y2XAXoS3@m{?T!*^|uB(UZ_n@{`oi(v#Rwa|_WPj}L=E@h)#BsMy9@ ztGC0o!^g;vpPpF#ob=nro!|OI)a%P{b)D!pFJa2c@gm{n*pOV0Xd4FzpwrOH+J)}@ zm?McFUmKsX;ysSmFGB!OeNT3~660t-pMdaaNCIF*~aN)vf&OzEbGQmYPMLR{+`}Y>B%qYjxIHwRv>fQeJ9kyJ0=WV`)$D zA_+eoA&x51>;}mnha5vn#Y28R46^1H^HIljxdf#}yph}zl=Qz?JIClc*lue#Y;3c! z(YUc~r?G9j4R&L@v2EM7ZQI7T`@F~Fe9t)V8RuVq?~Fb7y4RfZTD|wiwdBtU$l|vQ zBuR%bv?OtdF!VywPJjd=vgTgn+PB@lG^hJ!`zNzW*&mSv`Mcjwr}S~^mUBp0U>0+7 zSYVcOh&ZJO3GhjNi?wAib4+AX+to6n(f_ zH4c@zR$jf*)w-@W_(i;9kMjB{O(-O^K{eHyUTB^i~P0A?*zni8k2Sw@07~7a?+!d z+`FrHUr)oA(Vj<7iQ@o_oRuNUocm1e%k)|oip!;`m)!jPtI@%m+ypSsGs^d7Ggj$HO=#B_jeaK`BOciswOKaM|LDVHx#rHz4vAn$5yiniL{qv(^18<5C4O}?6A^yjj!hh|3{d2E5 zMPoZ#2Pb)BV~2k+u7K#QTadh29iUee2BJ2 z3c?4HK?990Nvsm&fXe($A&4d@;=zFE#2pWC+{Lg>5-(cahTH)P7@!raft4XfIfw~U zkM1>uq%)d-^K>?f51F7JM3(}fQx>u+_6TGKA4%1O6VjNW&I4a?A5wf!ve1w!Go9gb z>8Did#YrAz3ZN}t&hKKJ?&KI4$tQD&KwlD*)S^8uwl)oc``JsG^%--F_@|4>m|wAN zaYFw2p_ewVncS%bTTggR7u~x`AaDg0m&zrB31iZSM87HF;h2&Y><(xNb_{io2Nh+< zPHE8gh&zj2ZV6f4k83FR?r~hDNBtf5(kAR8=bf9+!o2rm7h>sRp%W@iKFFsFvG>$0 ztqo#k)|jY%XKYf5ozhsz&Yw}s4I0$m#-8~0j+3o~%d_>1jDdR=1_6DEaRD@pvc<}@ zz|X`$(RgIEJG-$gJ&v!>FgOaQs4vksEt`z5gLC?@AbZ>q4D12L3hJTS&dt+M7t%_G zK46%m+lGLj7B{Li*^;%jXJs9IlP;2+>T3C^+ibL@voHllx=`vH|GvH* zri?HK{5bP?%pEkRYv665Ns6`1ovRoso4y>kA2EJwlq{!IZdid4eUuoxhJIgZ86nDe=N8|ppO8O`66THn8fD3BI`ee5lhDB|9gohOul9YEIK4S=+cP$TwxNN zEsi@jEL3Ll32dy*a#X0pT&cgYMl|5lb=$!|K#n1P4NS)U}j^nR*!-6Qe zG_xpaiHTfrfYb&*HJGbQ@$wTg28t)=90l-oyHvZ|c!FDo2#lqkEOIIi&ohMI;g!md z@44LGb;U8hh9+P?LsI%!sDpm3*C$MMe+^+z8ik~dxj1sCuqr`zF zaffJS^_fR z>-lXn%54XQf{?9psl)e(q-j3w`~_%1>=(s3u^@bxKQ{6~Dr5?X4kNg77S6wWaJSVL zNa(q~qO66X4R1s7{9=6!iLB5Y>QTFV$9_e`b69k>yCOAD+meO!>k{W-pxh?x zBZAJMjJ;ET292o~gl?Zb_wTrD`L?Oh(@a5yoNP+7%kz-+R;BGX(|gdLxyVFlstK#DUO>5d0A0dtVJ809EA)h>P5Y^vj7EI>=1Aa1SDsANY&;rGHRE(2 z+evf1AY8W@Agf#gyY_orfc}LpQlWt^5`<^-sDMjax6`hcUloJ>}%epByVM`MTZLKtx~dOw{#o4F+@*vBgkua zGUK~#pRcD!cyHNgrH7E?%wWY=scj1_IsL@=lpZ`?Mh;Cb_L_59g5d(=6&YN1Hy9vclhSEDZ@P6lYp-PNPEiysSV{_X1Wr&EgPyBwe0kx<0oOp~VCd`PtPp57TghWvv z@gBgLGFtxJTTtfuGe)w~Jk$Q$H(uz-m_0VbR^rln0g#f1}m?X`CHk%~;3W!!~=)sDdTL0K*Yt zINN1EeNS|c?~fyEC6E#U?Vv&^b4ZR*-*!IU#UK40$GX8OqSijZAQi_g5scl^j7NgQ6k}hP;m7tfXp;NqenXRsI;`Bi*K_9k@xY3 z3QqkhHG`b$lc!fLt(+;~XMjdYp|`lJ+{3qnmvL8F*Z1d#L|qW+9F%9-m-h<}dVL~* z{%Y`!%q}afs6r4)SPM=9ODnn<9IQfX*}m&G<@OrKx{!o7JxwG!YpG}zMgi7^rTT)r zNf3tQlY@JKTjEc2>}S}7BXOHyIvraWKzo0>aY+3BdwC@`K=R=UEWOOJCV@_Bh0*dG zz)faQFpW#LY8VMeN-=KTPX&E=wfOQGqXE8M6BE}Wlex*11I|@4L3>0}0h9Ijg{gbb z;PUoJzFltY8f96_h*zEgtBeMQNnNE_rh!bALKieUlWh_}ORtf|B|+68RAA%ii*FS* zo`CW(Fp#6o0WTSMcl&nNZmdT>lTHA>@%W>Ul()Vij1JijI-$)*M?HQrp`w^+gC+%iq+SA?kd%m! zO_;pOX);q=K?X^EAB$E<4;zsd6^bpoP~%!vaNzUe{AE@I@ai-`&dDo80>8#$vCEW` z!v)(0@0Y{)`jY}LvwQ4>4{y#6q>f4Bn=%7b@N=#rBtcX7l+RY-h0Y%RMGE*DwIjkX`Z0-v3e-UojW9KRrIK?bkAXZu1rqnhF8 z+_7?t?cCftrVL37_MP%0yiU0spfh86gT9*S*P_VQe{ z&RWEMQgJ(`;8c3wKnmAMxhV=3JJgo=2!0=cvPBe$$b17jXH&MwG9M9#SM!i*0eyQ5D{68u+rZW8CYnu%+To=RQ+h%JMdS^#5$ z)_l(W|Jd!(BKfHfAT~XmyStAAbAk{14}88)=$#zYS(Z1h7XwqFO;{e6I(Y7*mg+$U zUn3ZS>Qu4i&tZ@hx?R2uYrhI4BBE85@~XuR6ayl&A30>9EO8`phT*0H4ce8LscD%i zL4glcCj23prmk>+9+ZSRBODrCOG0cn5>R1w-Lxm-L!Tu3ERr0eFTU+L*ryQbpgMGI zMkp1=7WS|N=9gafGizS$H*tQ+Dx{1x zlDHmT7->CMEYVL(sjAgPb}mvfj)Qg1FFkHgC-5p#6~)^idL8`jZ^XaQs{rj(2^z|rA8+P*dg~8zKC2Dr4-MQmzNToqiX8IM_6Qx~$q)#8_^6+tAgQw9X zV~X>NxYckud^&3wbrkmHXSso!@+R{RS=&}QQ=e~0to-ml65X_-XX{_8p(S_Z% zoJD152P7+9Z8*1Zz5M0p3&)S=(E)Wy`)_sWpW6Tdb?HC(`DLosYN%ppKI8#bP*Ui< zK^5=@tl;9g>Y)Hdl59mkb>iCjdM9AZ49R8Z9K35|l5g^3646aoM2@GRF2iG4cRFJd zk%yE+#GTCo}BuI}p?4|mH*zfZrlV8PUotIVuO?z~BzMjba6C(_ANFO@4vb+(L zoH29L>h&l*BwNoUt1(XfBwST)JauZ8b($@6>PU*}S2DL?Ogd|ly!gA>w$m_$@}QQ4 z_M*K8(!;g(}MD@Pr7|F(hUh7rH~xtUL-T zqujwhWr>=zWKd%~!d!3gf>BVATcfd7)$h}^+L}~Xp4D03s*05o5^;%W-wV5~^-<^j zxJGwxfNP>}IuK-ylL0n|1~EjazNk9iW4|^E57BbL{Yv0D&wgQEr&p`d@#U%nL!zGH zRNQ1Mz>|DNZSOo|D)Mu1Lz*Zz|NUK(U1_{IwX(5(3NFrw^f5skk*(W;K5thU%u-6n zngXw9DxVTgay@+5?wU1<6r>~`=0I&e2;Sjn7@Dqww;&$h1-&#TB){RhmDXvQdQUJW zCH}J`@46A}STA998#?CtVJ7L&*>9WZKxnHjo_D1?yO+>|ean1AWhVWVU)=b3o@m&0Z4P?6AGFS3KXw)Oyz?q&CcMr~pE&!%jV6_*(JcNEqbbjv?26w)GaHX60h{ z#aWW&o~{&ockF^{Vj43KGp_YoM}(@>Oa@G1>@g*)BL6DiMw2b!@OMQoEm9w#oh#3{ zYh^G8w2KHy@{o_JYt&c~)U;M7*h`YGunU`Ts7}n?h3HV*g>IdPMYGd|-k*p?LN|BE zR2`KuX}A-KQVvl|#>mGINILKh+m)Jylzc_UWkgG&9(K#^RMZD%2bimo3kw%}4FNSD zI=0mH{EFW+L~7n(y}$VGZ9ST$N0x{a_t_nFw4w*CtBNf<4WkF%R1;IB7rENSi;mL6 zv944B@3iH^w{w}=4H^@ zn2;Fd2wwhZ&ujIcH3a~4GvCGsD-Jz^C*KJ6zIF3OzWPO6AQ89+M&O%!2M@j-9G)t; zPO(;py1Lj0iMDXiroGLg*zoh!!OkwS7JRY~eq&QL2e(uPNmv(f!row~#HehJ}_dEAt`cK`B-H$eI9w z#_Vft0>`G8u#fn!{^RPvv5?T0WBf50Z@|%&9TzH;nG>Oh=fQsbBK3vqV0Dbp_w5n& z2bUf3YlGMW>S2#RhStD~!ywru5+UW_KvfJsTnWrPnpYA(DB|{``0yP#%%Zp+s;0wJ zRqdlN!ivMAGD76`Bdvg01`ROljUeRpHG>Y##X$;=c3`sJEIDFQx+)J_Wqgvj6wJnk zyJ)g?DUI1_@1c%_&d|){n-WsJO@b*liZML~{FuE189>}3sWLlMHCW2Iq!3#(rOdJg zaw7_usp47FPa4s5`9{*%HB#@AG)3*vzaLq$iY#kF(s3?R)0eHn+Lv#BQ+*C)!*E5? zHaKK%CCl#obz<`Our`8)TWI(4H`kww5x)MGWaoAv1Ct$FC#yx1X-ALx!#$H_(&MGd z5M6d>cJ~HB-I<+1q+OAHr*?x&e#Y2LECY2WhtLql@*%*vaSYfk(OOOoI!2t*SXny| zDVkPA`hKMc4BPHfKgOK5d1>vUXU&XZDG!f{ra{g%Rfgp|#1!q5Fzj5VFbTO@aL~@# zQ%oL81kS8uOt$F7r!%e=Dz&JhJLBZq*ghf)y)@CfGrGQe0|o`O)OF0@o<7L>K(1s4 zP#qd&ycH)$^2v+YzH(q2RCo8KtE99P>+GAVtor3|8{d|=L4d)ysGwEx*WrM-V(i9c z+0Mi@$->ZROj-S+uuF2XsI~fSgYkn5LS@j+;i*xu zr8SOp@(Sm$k+Gj=a>{54g;@ffO#aF{FFT9}qB+D{x@2&8>k}YED2bX@S(zL133s-& zouGn^<$iLlXnS_{d^aFOKNs8N{RLM*SUWy+l7yk=985Hjd9rXb9yYq3%1MX~gAVw= zO;Rngi;WuSUf!NgnBX=p$?;L{30YWID*gd$eP}a$Znj8_W}cm#P&DJ&Qj9-r^1eI| zF_8?usOw4G<54*_w=%f!Y(%;Xs-{U31Gylf>*hOD@pD3DtmhIvCGpK5s$I?dRy%AH z^6k!FNBTQ!bl9*@mb-F2+>q+BC%OdAXLDBZ7>O48dmd=Of|+s~9o&|)>jlR8HVc4cb#8vO-r(oZW_^vH~<0(=*;!iD5_1 z8HB-#_AvG~U*uaz!UYoH3j`g7!vh4$imBO|qTCdlOGu*Zlx&TXleqCiHPesU&sgA5 zeNdunpQ^EXIL!MPL*d9NCs%4${POvx$~MbXpJj+NvQhFrhJIubWn}H&A_lQI%;MRx z1h%yQyqywe+P_vR$OlGji9@%n6LA2pjQHEM9u9dm*v*1DHPNu{P>yVyeiK{T8E@tr z^-AkjyG=AXq%Ao5oM1_^29D|D<4Vqm58j<39h{sq0S+=4pNGz)o*SVK^9p9vUO24Bml>C~@W<0|Hp1V%Nb zjrzQuLJg!kbf#Mm`HYFFYZ5jEzL7ViZl~+zXH_bzCKVkYjXkMH;)K1UWJZ1{RgGto zHJ*d(G{<(w=9?E?zIX7n(gS+p^MP8GZaE71k>Z>}^=q&Z0_5K41^s0*=gD3JC?^&~X<2S>5th|IMva*6{QdZie>vO)=gVDS|K7;rt{zeyeOj4+Y>4ER0mLu+T&JC zW-9~G+FynJBUYI@P%N{z6xY!nbegZDF0NXSc1P-0;#7{H+cmj=18ONb(5d^{X-zl} z_wff5yKvjtd-%oVhx0LhFoppez1t(G!u@uif6t};%FdVeD2`bmpmSWHkQa)l)LpKC zR!dRF7V>-S*r4W_;`k=^;3s+)U^L3rZwRoY5H4lglb8Bj1+Ld>%n}a%fTQe zB4DSuq-F7?K_k)HEZqEzBUX7&jLiWK1Y1@RG2vk z;Z#&#u!k4IVRN!F0GQB?IXl};0hJ6o+s7wixb#oz~P;` zqS9HoO8H)Ym5dO94^SXjZPdD1jmM=M#md0UhgISfy+Q$=B5djqo)UpSEQDD;;$vVD z27U?su!)-4a?PjWCy!=0u5iRN2w4JAcgkt3LXysm>||$;A>dUmf{!^wUmwwPlR20d z+vV3X*^sj+F>Sc&B`S7dHu9Rne2btjN#VfW&MF9~db1M@jBX-l!QdiwnK`}*~2K=Ctn$Jrx_ib3^%{?_*2_M_mD$X-BYM#;ALmz z56G)ih9E-9BPG)Mh^W&RCnz4zpmZT_p#mW-)qWop>QnVkSOB^$AsjdSfWTRuBT$*KghBLHIB|vl}LtEA5U>y5UDit(eyrLHB!Uf+Bu4*DT6~6 zQXYf@lbnmp8>`!dpt59DNZTl7i0S@*3IzMTOuBZA{)Iu7po*>grbm&Eni^lQKLZ+c zI0uWGuXp7(?~J>9>m)?u!A( z@%@PoUvC6G7||Ek3Bp&Tek6x9EZ{1(kx-Oigh^FU9iAkJ6`OEG9g!qV3ku?^O+Zy} z86$63!M3xen-}@c&#(!hM`GzOy2FdnSMdRp2E3Y-r@QBpQ4T2Cp#U3ukps;TRYMd| zm40bxwHtuOkX?S&ABIX?amv!5X{f4f#r$JP*W9mPmPQ;>ZWudSb2JG75_}XZLW`U_ z5DO{XJ?Q#YikwRC;T@!9=sz5e%)<&vbXDH786&fyxX z#P!amDRl8+DdX%V#(_2D+ncGyC0;t+s@s0%?a~H2nkbwusZ1{Cvn4ZXCKx$kEO(mq z45h=2AmLTutt4D0En(Uw8Icc zwA~&aOlj`V=nVI2O2VsN)#KJV6s-0d>y zE7hIH%xSNeucJdEjI<4jt!RbvT!h5tmwy-^RwF6>^daJzPP~jz9z5V2<1J8j2vdnKyQq!f>0@|Mah#nSw< z6O+!@$1*o?M1|-o+@B#l*pbAOl~`SVZm0eR1T9g`M|fIV{@}$Nn7YmTjyK`!sr1#j zeaS^o{n2rZ9~<x$TZtHN zyqUMzcWp$hEV+B6Hxe^3xWpr|#IJ32Q&>m^e)}NyjAp0Vb?it52T*O{la$aMI!6>Z zq^%MYK{Wvit;QAMThpIhU0?*n|?8vzh3s7X3bSG8KAZqpYD_JTQy!X?_kSRacHLW~bWI#q8GHp?2#5CsYI*j=zidaUQmlYTKY+qj{oeH=NB=6vX$~ zF@y0)T07pQO89r){k7wX#&#a!n&5(aEF?n`>B!GBLUXM$o~BaH1FC_aTIE>L&mzgr zfT@WJq#&+~n<?b( zDf*5u#FR(A+P%A}zaFkN>CMOaKwWhC+t9>6m#|m)YhIIgur>L&Ys-16z)?*F=^YRN zv|O8LD>2b(vgswnyF*$4@YR4$3zkkGg#{O3hzJgl%;8vF&HIz@7R2kbuUlXKL6P*_H{zg!NeZJdgI1>`}AR?#MSDzpYS z7@_sJafKkQM=Qisp@^VK>6gMZVf1;o;`VV4Z#xR9TZI_Yggs;jT(yT;V-t!)Vg6;h zHj)ZZOCB;{HTS8c*%sZA$09AwNQYJw|EeVfo{>YGmDZlKOlD$dZA&0|Xi6gEMtjWuEH^Tb^Vhe`y_} zwJ~OsJQ-)xEy9vXvt=u$HhqvWlq+F&AV^to3TF^+{OZhGf0Ocv0ns!QtzcAu|CUEx zg9V|!&~9`xYJr59tL^-gl|*S4S4LJ`nrAdEJb8gikvO7;Bt0}^!ZastqmB()JHToQ z3PsfWC)ptSXYuYwrBHCUjN397TB{`+tBXP%1{#qq3-kL7@>j+{4>0OR`^HJN z&dbjXluFe4B(e(CfO-eV(~0G~sCTq@t>9l5$_zc;^xRdR z^O-C(CvRrpC=(R6b||Aa3E?fVE{R$;V@!)u@-cLj-K&=2VOiw0&=1tSpiy}rEnJrT zimgp8h)iB=bBNQ{aSrFBZ;@&p_q{En_44TUw-KN^oDF#0c z{p_XkKp7H)BF!fxdTcYrag5Mbs`3nOmApSUUJec_>=K7*te`5+{2?RDCwgIV%zn!! zVCJdLEAn_STM^_HYIlq&91(aAoxM_Uh^s0)nZen`Oo5fT1`XL~%%y?M-wkpLI!GeR zIkZmpRi;63kSLG^B8A8roX6KdxL=ykW|>w&ZEAz&cH?L4b?%6~?e=Hzh!&3HI}Q)@ z1}%SL+t0=6$oaLBU3AC83`E6I!>bn$nJ%O+I_JUtIJrj^$@jgS1bJ2?9hd z`T*`EA_HbI(=_F=9cRqSO_u?AKY4(NBgeItZz_b-F!OFg0jftAN&Z?vVzasZixZ<< zxbuP(ogP;V>d+Bt7@q4>BF_iYy4~Bz?3cB7%(HsxSNK0;U)hFnj1y4a82|P;%Kg6> z`~KWqasJo-(7*SvjWmE}EuA|2wiGBS)g&<)6y$eS)*mSRU6@)iQq9gw+OD?LckGX! zUlDyev4O$ZtG(F2#=X_kYJT;l2so)TSZ>(Vj08LQpW! zY02#6MDQNaTgx`_(Wz)jugX_Ps0lWC(9vl}PC{r1AzWEzmoFxN9DC);D*&5ZEoF`}&unm%Fzd6w_IZxlKo~GE? z9G7QZqu`v|y(`Hgf~Imo#M-VLYt(#~19%%I@PYGbxi zJ5Ak&zy*KnVd~`;Fs`rp9d{<1G%?5dz!AtCUh#zUfH(X*7m+Dm?AfqQZ!xfXk3~eK zQEP)CeS$v;EfEI&l^?AJxmjd|&Dg18Z)}nfTJm((3ki_r8^o{NlmfzTd5-*0av2wNYs_G2Jc8)qHYQ ziyM{`ut=PeW5}pwNnuoFp5%PaL>pI@a@MS@43&T(hnFt5!DlBvd}0ndmhodhF!@1X zU=7-4YH{lF+qp$jbZCS&JnJNEd(lfI{Ra2Vv1on)h3UDp+&oLG zpBi@l$I1@bcX|>Y#l40~)@2qBhI-sbM*%czs2)$t|omO=)DqiROAq+DY_M=!=1+Liy2xM?>!v94Cziilb7p* zqG#6UgoPYJNtfRyrIGL0KM+#pK(xS?>TPbP;%1}{(9(Wgf|ju#u}O2~@XU|2=Y$XD ztK5jVK_4YpVKIE(j|HC-XyNn-i(2?_ky{*|6Y)IL$>mzinN$S`^>`s|C5w*+-SYB}kr*jQBC*c+=Akm8MFDo`$ zR3K!8)9ki%Yg6He#x@$F{z;dG@{RPpva>TzJXM^ zC~gx`p%PI&{fHALB?@M&0iQZRWeQvYAwvCWD+!6~8ItbupnRB~-F+T)ZZ$vSM)HYz zn!pkSdHCkwTKpx%k)Kzn=tovf)U8G%BW~NTshTOJ?DcU->{YTNxZJRaaKf!)eN8X2 zunPHVA1??;%6_Cic_K2@AbGS)hGuLjOr&p}I_ViYQ)#a%qtpNjREnjGNNA zZJt8Uig~Ml^Q%@O0j|T6^!6Hyerth&l!%Z(_4f)E9M3vUn79-_={Y9tF6pWsPHoxa z@)7)4{7L&#pI>Xnc?irac>p`O8g11m8%KpR#)YyY7z3qmepuz-n@w%%)J=bVgIu|| zv~G%$U1igPka8X|PXxcXtnWdP9A%S4_{bj7-b$!H(R{C(w$RX4VzEwxUWc2`j0!gH zVd)L=lTtEe9zLTKKf8-*FLdO@Ej9G&YiP7oawc7a;)X9#WQDz(4s0Tri2S_xXW2S(L+A4Dmf)f1dig3R{&#nnwJ#R$i*j#t;T zexNAC`nY>pA4sUkJVpzXmr5Z5*->gLY!7cN2;X*nvoRGcSEYMP_oLxSXKTq;i!^d4 z{+_qlO*QPD^yDkY4j~RRBBv(s_Hkd5(UsMs(KI?$$P!jRB)pKdjY+IE-_&S*G!kj? zE~(K2&%&zJrGn0dvpzsPrk!+QvWN>I$0D;-Q(9Tg=BSnK>=zw}G$Fq+5t*}jt%s}D zA}W@cVlif=5h2tU_(Q{fV!!WnIZH;d+!VUe4OEMC(*TM))>_kms$>i7wD?iJN9<9( zj{?}RE(Jg|oo+G1!@aEH1q}mdH zk?$V~fceS>73VW@IJ^aYGO?}t?L6ou$tpyo< z<~pF(o=XQ~X4%B{m;BCH{Gz0c95H?C8i4PUgT@ z`1EDcvU>qs=bA+Vgp4~Jn@O2($dQv;dDd;oA zo;%G~Y?^#h&ga<&D$BzmU z4Hx3&{e7SC(AUiLP+Fj1{B}>tlyk=R?OU4c#F{oc=@N%^0inbAn&Sk|L=zBwx8Km= z^N8GOziRpM7OBhu0BfB83DQd2>rAABZ15GJc9jygkfR<#6vA#)$dq6xZ$}rBJ(-7v zwH}rghJIBPjLII+NF^*1q(bCG6h=9U)z?PW6QeH`B#Tz<=qU9RijvzJP!28B&{TW_ zQKx8pYt3ZIs<}s#xa83;%o~f+-JFGKUg2MsoYHqnn>aakn_Xw)pqgS(UzT9rV*QnZ zNY%tEJA1jFV>KO*JZLgNA^R-dPeGXmwm7e71f7im{Ok%nJcd)7vD5)j14E;EY$3{8 z7uIK^l00@c>ZL!g9a8F$qLX4;lApNM7%=!vX5rDjS>3^bsUrzjC#ur0XbU5n>;k4L z&)RjdZq_S!9i;|4PkrjVS_63O8$p92HW2sNnl%SIxz$GX39liknNuf&wL-pJn8MW3 z%Zo#g!4wNUy6P-7VGX~m)fXJIY+(fgjZ;& z6>5qmHKh=d?3c*ryJdmjTe+Ic^c|eva%$H6XJ`mDwu6(2coOWbie!PY^8;J zSq*rw6uZ|&eH>KE6b%vrVBOoy)G^kk5HSu;$fX8~TnC3G?O{@U&{)Q3%-IjkTCFZ? z>>*AXX?Ex!YMzOo~%Tvsf$nV@NG`l+Y^5*L4Uo@lw;p-eERWYV`tG}{^$D`*?bMEJo5I3HgaVwZ|B$yHxw3~htH*1>oXQou z!@x8L4VHn`Jq3m4gzc>T=!w?jp5_Zx1fg>;Cnn5^8&amTQ>#Hq)T zR>aq7g!|yeV&ytmRgzq82Ju-cEj65iOHDpvR2@)fGXaw_;Hx~toWh(yOZC(B?IJL7 z5_EW%9`O?N#&QqK*&*t5YU*SIbs|K$aFJ7DI)LF4VV&g(+hSH1NU(JpMG_;~1oDj%qmyMPG?9#1LfZ`F>?dvDn>M0c3$RYB|=;m@*P_9$(jxfIkHTP{{H!|Td zQG!gh$)h92qcvjaY431-RG&rgyD_vgYuga>QMXTy1ZTl$B1moa4kZoz|lP3rQABk95M7$B=9ZoiW7Du~AH`$%TWIqomh08C7%h9+Ivowv zIbX^bQ-LwR^sUw2Bl+pyaK|ocou;^}Tf!UD9b1;GRg~Xgw(2iRe+P!>*FiivT30OM zN1G|&D1$)Fpap7FCV92MLo@asVRg(PIf#QHp7O2s{Um)9i<~ZA8drx}@;WaF`L! zn&zQ9rMZ`eTVimgvT>dM$?h&s!C&a584Q4c(=y$L2=5mqha?m6+1vcA;Ch)MhJZ#| z`zlMe{q%C|{ZQ(1J)u4bE#=ChZNpJTG05l&@HzuGpPndIX=r9+^ZFj8`i)YQZDT-* zT+g6gdqXpb=T}vH+2h5tZ9w zk)+Q23v^EGd#p!G#Arv^*!fvAB+aE~L$FVEWw7l!Jk3x`QJv{tB5$`5sWV#*l)J&1 z1bBITgqe{Z?r;YJ1}(a`DI@3k&VKWWm!RQ^M<;(S84g0dq?5;_i_WcdONlpm&Mvk; zoq*4YIWjp0wM*ZN2@^o-7_J9jkg4qmQ*IUgI+T9)mD0bR(HfTJ75s2GcZK5>b+RON zf!a%4wGHz9<#w*ti@1AkNLr5&jfZ{6KjzH1*o9Qa$w3mnR!VB!4+du@<%V9-1Rclt zlSQLXp0tFPjOA#5CNn6CIi#W!HD8)LTrSs1}XI2bZp`g8WKw}L^I zU$5UEx(~%5-1;R%^1U}|4DAb4PuqELCy?%=odcx%=nnkmm5<&Jf)%EmaPEt~Mri)Q z9f;B{NI_%^E{ONgFJ1xXV+)s9SYP7>sNA4ANlF}@*~PyI@U2D+NAs1qz~}Ip?Iq$P;p-1(7-foCX8_~!#x)*~l8@L9E0cSSa3sZ&- zVx$t35ppldXnhbjVVr&@38ud)bS1VZfsiyxv)?WDxn51PqnL7>gq+6pa3ZitVOg9d zt)aeL5!4(Kn93?SlwI*_A3rkN)xY#E;CYm5p9ergv0TcwE72)A*EOyQhbcFN*D1}{ z)GPcaC-U7TK8r6N0m)*ueBV#SfckA?Hch46@*~9}9}C5}#KLjOP9jzlUn>D(Z{buv zKeEifsTBe2gtJ^vyax~=vzl297g>puAG~!9D4RAFA1HKOKn1MOszY263GD|LVgtVK z{kBzSRceI$Ez+{xNH2lpfOV~-Z3OR^t)h?98_%;pjH0G$Z#=P;!crWO`qjxRfDWGU z;!%3wz)gF=vUsT<6Ik!U67?pX@(Ca4y-QnLTMH>NgkxYFeSN_E*~`t~kKf?u zahX9=++9)zDG(j)hdW(7`oZWViNxo% zSn)2k3Hv>}*^f>tAX}^PY!RcanZ_#;hU>k~QbM5?+Kxko`y<=(_(%KF-uMHZb}!`{ z4&-6Rj7S|%5K&YaUkTdxwON_}crB73-9xZT8Te?^8a^ulu+Ub~I7;yTIU~&xblguYmMlK{j`N z5t%&0uOW{OL8d+eul!}a!UiW$L+!A?CYgU7e(P0Y;p`PLKFyGviunA8I|f8he|&;y z4MWrDixNvYB~H4W*to-dW|h|A2js6A;p8#kN;1vIk#fF+5tsd;jyYseF6C2JMf^n_ zYn(?j8pc$7l4bQq4YAq=@z7jD&GPg^V3YAI$nim6_|5NrOu`G~zFNyc2@+x;v_@9;+QNy-uRf9e*A?T zvw$86s{T7GW96#)Z!Wmly6M zI)d*3B%ex~26{vKYVL78`jJdDTepyE~#)y*nwrSF}fKAlAOFeqpk8_LxWk|+;W12#j#Gfb?Qhp|aa(f~|W?HwtW zU`!W>U^;_!^yEO-wb^ItQ3GME83G_|aqb9BxE-H?0O-nwnHa%%v~@|X8Xa$S&uO`Z zGi4e&y7XeAl(`qj!MfMx8ZGIeKze;69W?%|)P1;tG%X=*{{xvYqpl516@xyFtUTeO ztk79g8Jjv@|EX~Fm>3fVK|x0Nn_z;W|CQe)0d?(Xgm!8Y#h65QP-xVyW%6I^@eoaek>cUSeP_o-@r0l)7x)|zvUd5tbt z0W?N^H=3L#64Sy$o$;bCl2{#+*5zIFpU6F-UmHmswW$67%t0mjAds!2e-@~a-3*|9 z5|ElJ#WqPu4t;N|Q$e@pYM}yR!=90qmz1{Y#oD@MMWc&&$zC0qZ1AY5%QA#;Lr&-m zbva7h22joa;&9e7LsA{p$VF1>wG1%j$cX_uHY=5fNY6_MrB~$6u zXD1=flj(15wGg*~iL=$p^g{1BqgBrr$T}Gh~q0&l4t)9vN=|%bonBx2Vf9y~hedjT4ho zrRliAc)-Nm4y5zqoTLp~DO^)CFKctD`yoY2QCJ|u=sMj? z@A1N9-TK0msPV8-y>h(P35q<1hQZiG*31JbqTavGPyzq@huU`=6B#GR|9E&6*Q{s6 z(B8{B&OpE=*z+fkk8D(6 zAzBsDt`M1!MB|HQa8`T*+uaVv9ULH-#=!5Zku;YN<9bwXJ~uN;c-Y^Mle@EU#`77= zNdkZ4d^Js;is?7R!Axk&+hGdjG`>pW9M88C9?;gDlbvRmi2kY~S-;#f%mSsXhehjQ zP1#JN(lmpD;I1%O)6-Kmtt)0cAV#KihvOJm?Cx}4cKK^R>WiHUog;7;wM+NJpOeMS5#Ihm2%lhs6t~^1a;6SvK8us}e>-FDX&>6$autnTCjV6z) zQGq?YA$-`-ev|~qIkhi`)kQQ3U8%db6k)4T!+AO11GQ8u;dPdK!&3>l;VfFEfLo`k z=}|pv1?WF_bDlJcTu>#-WJ-)e^|FoGw7zP5oO5Xj8p@3>)i$^jbxNnfVfW)@)&x}j zushad8{7LT2n4^^>|2e`Jo-(DW;|(DQqwVB)!FC)oI0ydwpo%Egg{WCwqAn=+n#YU z9yaNqOol(<$;s7=o#`%^@pFW$o`J1B8X76^#ce9i(ci?dwkX0;6&nAAWj_lcU8`oB zyYDj%-nAIO620X@hfaSL+1OL7+@R)kmP zY5Y11KRCszk2jP(aHe+?AqQN{v%d;h+Zvs3 z5UMyQirVxO%0v6EqDwY}GQx#Yi2zu%UP5fL?`mwESRZ%2eqFpQec zU-`E|2pO(IAb^I=zXCM=i)u>V2t>a5e-u;ym>odm8&q+$H%{2^SQMeiLUvBjK7xd> zMWyh1dV+>5I90~EqioSteZGvo*>ZdAj~7!OF|ylF8ZQPCacaW6@i^B|n+i z@-5s~KL;GSq?VNV7_hIeaQ6(psna&lwi0;)149*Dyt1y3I01VKGP`5>R#CE33`1t# z3ufBOkAp{fOOAVGW}D>JW<^*{5nMPWP>C@t43V@wScQ+H&(IXNtD}M)jC0BRP~)4T zz$F_3ck1D!lC|H|$9O#{uPRfNvS!NpMsXUTbIbj@HQMDS->J%b|7{CNr6Q^OW08VO zYDQOksaE9fc^qigY3VH(Z5es$KN!Ze6?vk1lUH_0=ga8+bNXhPF};@&A?8bgLbxG; z*8roy39LZB7*p&Ov&WPibT~{mrF07yRd)BIlsSja_2lSs~OW54u?_0OtpQDK@f=jhrZ>LHXv_(^6 zxmzVz;0%J%FK6V#7#xZsAK3XaHHjU5&?jKX+nOdaJWM83x}CDbFY1a(YPVoe&5bAL zq#8rQp(r+@AYZ{+1u&8{;U)uzq;+xZ^>p74CW2tmdHy=KLoUHR;-F)j_^-#7<$r&- z{sp}O;f?=6_F4QP-Ed0@kOyov|Kgcsp*fC)h!xg#VuKOjW3%{?@FA4`ODrqGCxBf+ zZOwo9>lS~RE~7ptD!qY@>fNXzm|oQ?HSc8Of%ArgO_oPXPnOU74d92_7a`)#&nl=` zUG|v3YT_Ul6j#i9G_;+jEIxz_DoQFYKkIhsh4ZBr%1JJOb z@QAKRzbLHjz-j&FHVsYVKdlcxBTfN8UxP6-yP%8L2GgVZj&0Nmf`2~vSxzE=xi*mo zUW3iT4bH9+t&F;}kmDI41P2pR&>>wvfru%Sc(zD0VM|#U(7>XxaEH)A1+9+y+|t9K zRCB5>ZR@{adB#vUcbh)eX>KdZ-aIx9WYe+?eq~}Sg&bp`bohHq(AMF_69il@wt{lH zAN6&2Sqbe_-1}7c1Y*{8bY*Z>^RukqUpdjJFrOgu|Ip+b&7xDoQly-sc!(J+6qrw> z9VuV7`-u${H1Os6z?}@Ilrc=Hh+frwFZtj z@v884%0wwv4+V9Fo?;(7meOT$=tLD?HzJ(&XPi-6qmlRs8PJyC!k;Zcof#C9L@`8} zUfbsOR!PLQqTz9Rw6Rd56~~$nv{d+WZmiAriWKyf zM==br)OmuyI~qk5nnsmDdL@F9av$+7N^h4?OX2c}Fs>m+!BB&}r&B^ts7eVbJk(5k z0q34TTESM7MtV28W^?okde{X`vS`~Uq8B;%@3Y;*GlvXr@P9@b4;&_?t@R9pP2=mz z{vwv&H8`jBhh0V&#E%=4&*8Xs&e@?yplX_nph_Z>M}KXXen)Z@#s7_c{QW(6aECRL z6FkzG;RM={N)&o2c|;~G6?p#X~jtwP8wO zMTEr4Cjinf1f(=KbuOv7FYDJI;X62?rURHL`?i)Gp4j0J>rH;yOqd-${zy)grmvsb zKB@DG38+Wh-gb7NB1U-jn53zO{`Qxmv47|+vH?{KB>!41 z{I`nse`9y4ffB3G-q7gn(E`b$`M}H&bF3^WSg9-+r5JrN8pnZxrV<(|)qy0oq(oXl ze3&gA^}N~dIiDR1V85pvUC*Yx6-XBzNYtDzGNBfH<~&#j`3N@DDnY&6g^dsC-7Yy1 z1QN9bwNj-z3ggn0piVM*5Am;?DCQL@{VGXCNkQO!3<%F_y z%soVKrLXSg^$)otv6S!%|WOv2&tB$N`y zX3sY+R#LQpvkEK=!oOLSG|5ELxT=j7)BhU^B#_s1n; zr=#*)Ev=(-OX_lSG^<}Y(AtZRaMCcM@h)!IvM%DCBv3}ccBdm34aj)MO^4u_m9I(8 z=vV}~W#z0Aa*NGA&z+Q5$xDd4&-@kvFuARj0sQ0>ZcoQQs6}+=;xIVH8KGu$(V$VA z?J8TrC9{Av;3Sg z#{#T+!ce|!;4iCu$*P9hy?8Zm&GD33$H9lBen&AbX8Z3^l31j(kR^;>qMF#UBQ16m0ve<0rf0gRUK| znMpt>F=AI3@GOoP9VetuF3kyru-G{Syhwcvk@{$gSj)FapMN9gIwi}*^h9}hj>cu# zK6p=iK~|!8MK})x!}kh;cgyrbgrg>CA)|gZGA;iO503PH?1A<#q`K&{Hvl zV0-5HI+!BLy^Sog$-@phnhPD)hFYBn^F@eNC$8tr>_$7zizD?wctVgz@r&5^0`dmN zmnKA=n=4_>`%7UouFIr!vyLl*KTVz$baQt_kVk;%UjY{E|J_4W+}hd9(deJLQAtB# zMFi~)BityQQunJbGiGBQ6;_p?m;`LVtmZN&OSFD=J{$FEIWlrANjdD1&7nsjNVg3O ze7^%`Mifk*(l|3R6AZD##mE8Jz_qz^jt!0jF3k7e8#bNb$$a&XQ~4vjeN$1IG+#F} zHF+XPqR1de7|VK*`@A5ts8l0JA~g{pn=AXOBO};(1~9K36wE!Tk}7wv>oq$3Ke}*d>rz$ z7`M{hPz~X+#Ys!871oDT%|ofvL&N)hvsQKCiNzPa1g>tP&&urv4b;wXtF(KgURt(6 z;ld`43YfJBjX>@F2Q{4_w&2L}nX7>Yzrz;E)vevqocCWU%?d>8hj;7I(0Lju>1xGr z-P_CSj=@gE>X+b*++VI)P1Jyn8+P^B?t=UwC%Mqwm;3OvNE56S2lT+fGRq737t(2M z5X7P@R@p8@rAn$;{Ttr@YC?htAb`5H`>0tAd4e=gtz^6ihBG1oVR5{KNCVYh4^DjzcG!ZQ8wkI@V{qY^1o1u|BE}UZ73T8OhJ$}-1_%%Y?1RhDJ z@*?HpXe(fK6)U^L9ze=zM$M;0ug^R#mC3v0YE^Ox5m6<8Sc!Rt$tiVrWEnp}`VDO+Z2vnsMIXST(dl(g-4Ju1F9MG!*Iv4vS1(5^7Eu9xA9HHl&97TuHF zc_G2OF~a1A@T6E_CHXQ*4dl_67%edm+%+=sOmYUVp$zeMi044o)XKV&b@cVF*7G%% zwu3Me*qW5P$XUizc@mkabR4hH!8ksaBkt#tYi6PO6b~Ho=V_8-$YrwCs9hzY?#OT+ z!IA654O5@ULt_CBk<+N^&dDc$zJX8ft?W9FsON7iE7)6@GXqiA9T+@EPJu!+Xa9sx zdEb8h@}(7F+~uCUJLN%tn);Cbmx>Q@e zUCO$4YBe+@nONl(@W@3jp!sn`%*vbB7^i5&cmVztsgUV4B!1MSvFwxI2`p8#(2r!8 z>$uC`4k#Wxp#9vU_K3EJz~z+uU3!RWw+hPLVdSSzxTiF}UXWoOUXTPC)$i3BDp@ao zd1N8CoDmy9Pg?bV-H~Me@3c|D>K|SFzhU)R+t3f;)fMF6eMuG7=@9*pCgs5u|HkT* zhy5R{J_Y*@G^YV5#1Ukk3h(T_@OA=4LTj-iVSw!Wz8>e99up4ZUgHin53d&lK41(X zYhu@}yzgSVNbq=AzjD8K!0LOWY#32AO01T}-+htzk~S};HOin# zz@f3AHgQPq-{N)rO>2d&UZ==5NWl&G#-g($oiUh=a`E`klAyu#SDn5xl2xIKrASdt z@z60C1uPrWNF*wCUNr^!%F06JpZYQ&yh=jl&hPiGBt&zUa<#V6Hj+vT(D+oB&1qGj zdwyS2Tc!WqGI?a^=xsjt8W(UA6%MR#8Zld9L+WnYZG6q4NXs>sC*cnl856bx8(1N2U9%OP&EmA9MMRV%OF`=VzXZloYdN z?xC^RAQL7_DU(;GtMN58@G2yf221`542b4gvJDuGwe|N?@rMFvE;AVID0EBAv8-%S ztt`~_HVfwgDfF8bm)9PF){2G)CP||;e;4Xxe+u>bCLK_ro(IG1fsl$WG7pX?qeN#% zk*|-NMKh^*nq8Ki>t`U}7ybo5yCfGX!?9>eYJziJiJ-z%;WPQ875V~WceGbWke@1h z)ZrvqrPcX}a7oj0OVh02Q*i3qCyWt~+2}08|nwk9YNP7+6rO>G`3lix>Xk8-r8?!-)nDlN4xZ?~(P2FAT;dnz(~+IBXlF4K^&O*e5~ zhY_Z2R3Vb=kdycp(mIw0Me@DP@DotTSHwFo?GpNv4o`5@$akI=?+vl!nM*RmFm#-~ zdR+T9ctoR-Yu1iP>hcFN31jhn{0?^rnfK_PbL{V^jf~DuSyG2W#Z(N*R@oGl6uToM zc!i%O_Ss0Yh#KY!zYx7S?5j9>hPm!zI(`wm0DzzulS_s8#3>v?3q&nu&cXQ2OWb9& zgl8Y2AV>A$JwfZXdg;y}Cj*okHM8hC$Xgod4T+l3v{fTilH0j()cNsZu!l$R?1%t~ z!*r{Nd-IUo`thUM@RSJa{`E|r@#Jr)guY9lBw|lm&QR_FMJrw=?UPQd)K=ugTRg!E zjSTDEY`vVBhMqfkvbH`z#)6_awO@lrw@XFTCRO$+kNum62tnb;_Pd*>GY(j8RHT{N zqd;YK&y2g#m?~rcpTes`67H5SNUzXzj|qgGevPj$aIkqS?`<@t-GA+UFQ&ov+k-TX z@L$XH|6-`8U}j=1{y&DR{{Xf@&IZ^pO-vU||KQjF(y&1|Tp>Y>wWcj%Hqf%SI4%$o zU}s!Y^Ss~c^Xq*RTp8A~IWdW%)boBrHL=iy9J5=udavi^h{tREalHOu!pHjw6cbX? zk@b%AKpsxC%f%Rc-*?SN8_1AYO{~WfP|rZ>NvNmQErO*r7*s`dsEoDpzz`l)MG{E> zA0g9wZ3dGSIMjcD;!Nh*g*1#_gNxhx=z%~xfi!fKYDSe>U#@7Lo>SUVY2lgg+%$j) zT<`Y1786%ND^FjqLQtYH5$nSkIwJGdVZ6uY#5Dohrf4*W>Zd-U_}dVRz<3`NG-hKa z3^(W(4QmJ5ga)3MdCeJ$VLJXyZ*UDvLfm60SDdTs>MXIFVVX$`bTUaRS9``aR~xa| z6|~i!Q?&5av);}PQ`zcmz}{VR=yJYjfW%n03Ms4Xm69G;nWgZ|NK=x)wR6#QRgyWV zHy{CS#4JQ@)(UQX>N2}Cmz-(oJ0G^43AU{<-WV^%r zq4*P2A!+9!QEiLMuP_198a=AMpb?3=ui+4ZxjLNG*Cm&M@vX%b7Ue_7S;20%B8^Mr zY@2cfG0(q93OC%3foWzJjPzBz%2b@G2az!%dKkCU2EZ+2Ib{oFfB~4xz#Gr5y$Dv8pa5kRC+RaUkNsv=eVFziULK#e&wPWJT~^uVw@c~ zo6#!D2#mD^}BXr${J+JQ&FwkP(~iWPYs(A(Eaf-sn^? z0pxceIv*Si$@de)#&JBhSn)IK1Q>YqO^?owp2ml>qaX|&xaU&}Cn@#+9xkqZHJ8VY zsDo!86MS5%nT30^VxH51c(lJ^)Z#?{v1*yonOw?Ps)vSAbARpbTQW$?H^ z9?tU5(sQnMH1AC#r->Y%lL?vO<b zn_^$UV@#r*-33h!UsxWwx;Kd8Ul6wpy1alD8Gym|7zIZP8M`wy?mDhMc!9-n{+R^J zo&;L*m^!T?;y`9`$e1px?=du$sEw$2K0kH39WmFd;zW4ypNwq>#(pKIRq?%uK18m_ zW0>ZhN{zO_QFSAIgg$d*iDN#r#m$6dG@%IDs-Q@Ix33CkOMa);{hg`nJJPj&4F#_W zY5CEnHGjONrH42I)Ja80Zeb=gZEfBHHtSJuaXBuiXVGG4^?VL~vg|R;mtigveEn*5 z1vrMu1f1UVSFPZdBQ=ZUrUGU*GRa+x-D_rD?=_?LgK2)`x$0k|fe%-;HxX~uh~%Rpkt|E~|c|56N>bdz?2P zG=uD?)(^igvV1<;!fptU_p*JZU<{CFlh%_9C8J8rf6xr2k5%nTf;*?UR6<#E&EB+t z+#_|Dli0*9t|wKN6VHYFn)G=uBo)m9_E3EIO03HO(g`S(!>&Y~U}DV#3Kv-SGh-J3 z^wucb*aGn_#+V;84ZIRo5WxFHmw}0u79w;yC=J%LK(1**+$2n z)<99;yDBqoR#M_C8q!EZw;pPp+~P40Z9SvKCy)xd)ySGbb|KDds-dh#tPFZi3(>ee zbh&Guy`xm;^hm1nXjNiqUTHYZS<{#8$1n7n!;N=bF5@x4GEZ>mAX3*H!Tk)hVfet*KhJDJ`*dFq|IFvG^0vA8Y^i7oYCCoJ%#gFY(_a)}^%hI;S% zvJcI$g9F)2=Y%<_Ju*&fhi&Ql54DYG~TsK}=m2ppDSAPrbmQ*P?K-4ajsw^jJyR5OZ}%+`Hq5w!d(bOv0?+7Bu&Da z_$P2zX(OXFx3PFWL5ATYu%ds$;6_+X}-~X8Ztp#etTdKu;mzAxeOSZ^hkvzAA8N}rjK>CZeVH>A8WA#^#Lh+xh z7avf7t{e!MTb`AE#r9WijUnb&5AYuJ;-Cj@oN^pkzdb#J#znhwpARU8Wl*rX?Z5cD zviX9A!bvc$ekb%phrFP2<@IGxGVcqEO;}(#s;=1liNzz0)C~l|Ii!H9 z#qveUE+cH%swqTjHQz-|3l`_YSxsP$5P>nFMr?n|VbER?M|u7MD@|ak0;`5q^`5PV zFVWO)uAyn90(p?FdwA88gt9*&6t%L}Uz2ZhsEv5|qh2czC-+FC;jz)`941J>(p6MK z+|+3c4YAZaNkACWNdZ$5fo)nw%F;5eW+_2f)7%#2Fg3?#f~{#Lmy;?=<7gP~EHiPY zJpDuwops?3V|*LVU)Ko3Q-?PEb`qU#Bszqnf~`kZ22?+v`!Z-gC&9#5kA%Y$9QP3#C=o}F15?r z8itiRv9gno&2wpL4BgtM>gP^=sjZdzsNW3=QLtn}kZ1Yomy+SsG1%-(LroHmmUMG~G`q^f)yu7dhOsIM@&P#nQt>oaHnWp^SK zR|<-9N&eC&nWeXAiD10kW)}qDNJaqKO3TzHYmfa>nMQ+@Fr+S(U1wiyal$KxxElx+ zxnwX|#!!QQXjozv@)#dFaoOxgujm<4i3Zw=$8n%uPYeU%^xPJH#~@R=C$MQC*SRp| z;Z;b>crK{ehSD}lcAr0x`IfN|m`Pn(c#auDezYeIZX+99OP7OtW3Jf3D%}R0UYjAe z%T+J!lL}2fGMXg2#ubR0^U39mMCN+4oz_iHu~wnXk_!r!%?mSDFOVjfXR(suS-b#M z(Q%`{kA6eHv=x4LU=@33;0xY&!b;)~Z()1V9DyxxNFQIgBrd0)vI=k(@o@d|DujC_ zS$Z5|i$|rx>ZjuQ8`}?VDie!Wz|?aaGk^d>yC<{%KDJN}j&jRPT8!e=C!6`nl>Ym7 z9-$5OeHl^$q8b1ax8JqL{^wqAA==+q)!*1|lP>_5ui>GZ$_x-r!3!T)!`_?k zANe4Gq?42BdWBBw+~+fJc0T=AQEdG<`{NHJ^54Dx|4Tl{KUySYWNGAR^q;@})}(*< z9RDv(5=W-4^drg^`Ad>6V^_r+5nJu<3*>$Lci4DA8%ca7Xq?U>Cln?xQjt5&ts`LK z(Q>?o7b_f~cAvv|`{Pa}=sm~~Deb4TA+uKYI)&k)kMLB zHFyh;1q0O;^MXvaJ5;@bI*JAHV&IbB4{4qyp2d=(7cSqkxlKV+ALPg}{5#sE??kE# ziKzquoNT0GFRv&pbqus+q_@i|@u*d3%9?6FgM3FTm`iS$Gmcj%_G#+|NzVzrjGF%@ zz6`F?#%^V^)u}p%$C3O32edB9RYTWd2`9=aRDCW*MLI+?tws!>dO?>HIY5|+{*BN$ zjS0nGCoFeXWLTjHX@%8h?MXKuIFs#3l^Nl9gzxjq0hr*BF+7doX>&(gQHEN!jrnZv zT$E9}4J`rs=-Mg;95ufHJ0dR;)cgDmnyx-;qP5bARh-~i&O2Ygsze&53|>@T0%@v& zQq;rO-ongz{2WX|MFJ)4{zsp38s`9igb7-kgcN)Vqx@J9gs}B1XW0{%kT`rB2Z0G? z%r3gZ-#yks2+8S~m`8C0>5X=XXPebV+**4TpE_^9m0Fp;H#~)d7EVRRc5BSy$baK$ zDhIS#C>?yYj(k+Z17DoBLtJ3-EZQL%m34uVtAwmKaJ8OMWRVi5ncHk2k#E>&g40hU zaze)KnNyCzNE2zNwgl==m8mvJg+NtNJysFb6z{!4orRY8fnwlHV0kSb8Cs8(k|Z%jc$8O>DSiN;3$T15%o|E3gY-z5_PDL zd(Pv28bmH$^+=96?0;=aJD#q${5#$S=Z2y3n%k-UPyUoNF(}@}2XDOfVK%J&<}+kK zQQ@;4dR|d$?t!)w$Jh>Yr|-ehGgvv4sUb7aWAQU{bjQtHJKd_#@EYzf$&N=02=rV= zM$drB%Rw0!UbwcWcGL~YF{lvPE``p?llw1#;EASW)Qp9A$XT4GiQnDD~$mgjWE~gN2oeC5J71T;#$lgxGXoB ztQ1+obassQhQgi2@GFtz$XBL_N&27_?@0d+(wHp*WFAvwGL&E@jNsT?;SgsuQXC-+ zvd3@y{ft}iO#9FM=u3kJj*Tq?;P>og&OzhSqOY9fUfGZm^xqBa7ZdKNGPzMDhq(}b z#rjH!>*UW=-DuX`iprFtdHXmX6n6l;(Pq5SaD?rGX`ty|)PGgVNqzH1bg+7{X`0J; z|BI{5rT0~c9wbek|GJjM``roFlpYj36{DP! zGl?YopZ5|dEKT`oOzaknkL zn@L&Tm4IMyuVhs&p+_C;gsOW{_A&=#8AQpKs8(O2re^>V&1{C4@N!c01p(^mM7ayt zUzKTFV=z}Y5j9rE;7K}_mo{1SmLP$KN;b)$iKc@Bj)bViZG_3NA+j}1j{z;7zu1is zAt`gycd@D&bsd{##f8d(>8KoKK!?suDv1a(O>wiY9mfVPgUKLRuHGnz-lQAs6OQFM z>~Wz*Y4Q$7=-Thp7B*eC^^y&fCfnXi3CS5eF75eNRWvck2yFT=29AYqG2u`(K z416gqFhFD0>&*CXN}1x|5Jn-VT5&xPJ-0};WM7@jp1wDJN~J5p>K~x~x|!=Iqv#pR zVZKV(k&yihfxaxQ=F&_rKP~m_vhM7@F5n@&2=Ttlwj(a!G0}RqS*l%@)gsYKf%v1U z`Xu&fKIVNY>E11~@BVT&hu-eD$t%Ym6t)>^o`FQr%gS_7Syn-Ape$6we)B zQoWWNQ)jhP*Gl0Ex4f1O@Z4lxrW5hnay;q+UFGQZOP&dbfT%9a0spPN%FUM{LH^%l zE?z0?TJDOrf&EEh_6eZ;yt*vz?2wjlkvY_uYaE6scWo!UP7Fecp*_O*g`+5(dC|8>u(OCGL z!Ygi+rHz=yr0D@MYkcB4pZyQ!cOTlJT_fK<*(&2ee~fVd!Otfg8{X`T#u!Pf)+fjJ z$xY%GM?5b0CE_C_s>vl3oyQ=v$OdV}GvlXc!#)uo0mn}NisXv{$#bZobp+Gavl)!f z`Ly^#SOXe(@!~_`!vzdyKcydv#IeU0Np83i1g?L?eYB00?-Xy@aYJX=0Uty9DA!mL z`*QG##GO}H$e}#P8bX!TyOZo_lwn@>CSCB-e@r9)c<_Q$@QQ>6xAo!Rj_ZZS*C%lB z=7mPFU-}(i>i0X?pAK9r2dB@UaYWv~jw87KJ9+ziAzV<;{-3-6g%N8|W-oVK(oFPp zY{TTVJWe+Wy`F5*G__JbwIlM7QeJ^!Si#+()82AdW_B=3R!^?whw4r6htJ;NcCXQp zW`|(am+b%-PzP=uuyeuN=?y+90C;^9ZPohW2Mwus$q*$9xqGSFg$k(z^i<)(LeRd~ zW~##P@mz579Kz$+y;EgC`%cu#djFc)6@gtE0s2w%mNCAX$jhI>*KXu4eA3+Vk$>FwtUn zWA^H5T=Z67iq3wizA0Vi@EK7WQSul!rG=hGeaZbzQCt^7y@`anR6NFhG8HY5RDn79 zqz4O3NJ+p(<1Xo~@K2t@@loBP-6hfiHXn*w9~+>sG!0hWhX<=p5)^-9j# z!Dq^?b6@qT2AAFnM@&>-HKJ}lp@ckz{6a{Z`60$NeVb5c=^g}M$Kss^ZRGw0_lk*N zF=!C!>FZ2&t@dP{E#{m_Y;+y&+0x4saKtu*@e>K73aei%nY|sQ(Bvd5=7}A0ko<#u zP!rg%Pmj!xnm->Qk47M>(S;3!s?@vAsw6(SR7n)EsGCsQCv`YkY96sUaI|jpf#-H& z&Robd6UrY)>{A#PJ|+a{bQzCUZoZ+vmCLqX)BpK2B|~}pnZQ1LumpXcvXbBsXdhr; zU_LNMk&1!7#{c>GFF#FDBgemb6#qn78_fvY8^!~fCg<@hhe0oBVRqWrL-22aS~@^!4lR1 za;C(XeMW6g{xiT_YDD!kjI*{mV~Mh?RUvoZJZ0q`TW5OgT2MKq5CVf)a3?ID?^~9P zrFgWsC)LWlmzXvGwA@DqQVZ8ncQAT?zOx1N*UF044{Hzr_<^9o+y zx%U9ITrIIZoszoM0>nOWo+L%6C(B_T`C1fE?W0QyODFOa-Vj1{j$c&+bM#dX?z75V z>d)eEXvM&UpG>885z^E|0%TO~sY0jy->_UDPCR!LnZD~ms#H~-$+UXyC5{doz%JmH zpr5qa5&JT)dW#E8G=nuU0N%AdiD4Y#z*v4#>oH{7B0J6%8EeurqQBY5(Mzn@G1CFL zNHxVvai}Xb@@DPR=t{#c(h>qood>tu{4^d^yC_A_!u5puDd2b@S+0Nh{K(Z2h9yY@ z7Fma1QF4T=jlaJ8gpzeSenXgQld=zp(=H??z{%U(aRqRp@in7~&#j)i}{VDA&QutyGE%*83lnc8VBzz+?3%g?t< zWP`C~&g^{Q9NGRMy(spqkubPJ{;G^HLofFMl=VT94ei&#IS3_eG4qYGCCw7^vH$8@ z1l!>WxtjRtNm2(1$rdCI`a&QU9sCB}ch{}I_It%Mb4fVvAbfWpP$4?+8~ay{Y)X^c z4-H9->8HLM3Y7~f|MC)4gM>WYomFtG$oCe!2Lv=c?-Ho_Eo^Y5y4hb7hGPv;yQB>FuYqzJi>-3-kijsU8=*X3OGn+&liz#09UqrESaS9Yz->kQjyu7h(++Ud1 z&9^2vt?F;s{)k$qGg-kOLEHb=a^io9{rjiW0TQ);e*K?5$6vJsFxoQq>mLqjeJVzM zKy(16uqhHE+KTB95cIu-oz$WG4|wH2TqwvWf@2Oi?z=BK9furmP5&NGJo4lBb}Bw6r&CeQ4313~%2+2UN`g%Ks~Xep5Uq!4Lv)}@Jk``E3E zADvOi+Twa!DN84m_Nohjx9sPnushs{p!jx#t3CT_`6aqFLtQeUuhEDjDcc^aO&x^b zJCDm9HgNdP+Q-u>9p7cIL@Jl)MP#E0bt)dvDD zpN;o7ETkdq%-nLA;@@qiS`tpU(iMBq1atLgrM_ygl69hN*HG_vC~3GJe^RAAYzn2r z!?Y9cX^6aMl*zz?=)^}0-fE^(K>tcst{ArxDYoPtM;&Z|u#goSza?N}m!vN8liU)> zMOJIR#F~$nUAXICFqp6WDXosioJxPFUd_HY)}nT|MU`CMNpfB6=-~qw4$rI|CV|<5 z>HtOZEMp;1%d^DaAZknEhup>L+SJj}G3`N|1E+TP2tw`q87$#erefhFN#*hRijdT; zK&9`3MBY*&GY&+>j7j+p`p#GZ11tyb-E#W!bzmVr?&+8M>g}__SkBxNk(RXM{hR-3 zYQ$GufjfzIRv}Wcj-0i|z`~vmHO|Xao~3{4R3(~#yxnDZz5n1fyF$|R0cGVimrcuw zs~okiBmBUuO+ZJ*%$QjexY)=XbHjYRj-d_eZ~PRwA1j0{iQ3JLnMkH3XJECieQ@W1@Y)b1T; zj1@GA%r=Q)unjOVbqc5*UK|4vS3G`vv_h`jY)RuoHuw|G16eaHD}ZYK=H;WWDtL!r zf3qa>Cz^-x=z#rL5(V!aKPHGfR|s6L08GxlgR)wdU!UKQF?Isg5UTRY@B3p1tZ<@R zK$rHt&}L?Kul64n)Wo(EK@p!rFC=liN$a+l8?+W*rU0x?PF%EuFH*)j)^S4DMMM|z zob!5=Zl*JO3~Oczdv|jX_n+>@7I30c4#a|j2eF_Y{_1Y%rS2vvj(AjVcQm%y-`oW*Xg(ByI@r`&c~tf9o^ zqgAcmHh6&Ot;h3Ag~FH&JQ!bXKF9{MAG>4s|h?J-m#9PpBObc}(S?^oE zIX-4mIREs$o9&>%W-TXlL?HXN74R~->9qjUSLz!rfzvuK%|u4tXGN9;Hz%iwm={eh z*s%3fVcu`31q{rL_M`lEAF+sBBXb{9NzZjwB)^Ko>FV7>BcMzM z3`KJ2{9yRD=%@6^dL@a0|9Y;-=L9`Sj#2s18FQdMumdn@mi}`TN?3$ZN%vy=x|aD^ z9PWUD;9xLG&dp9Sv)HqD^cj}V7=h3R2LB^#2tb%oqufj2*fhK;)lf9pv>^cAE{XQ5 zeu^v&vy8$%in768l354v7PVzYbjE4ICMKK)B7B%Yj+J6LftlIGLCjIXM1LRq%hnWl}Aw zd2MysPBhA>gyK|`bvdSD)qf8PZ2!rCp8*XD{`lyi(~Bpo0Ue(9{n+m@JFe&FHPO+W zO1!)4tp}OEza8)#tfu{^EGX)Gb7e@A)`Mw`#p>0E1A)sRh0!IcdgbJ8HM;EK#8>67 z8qzQFEDZ#$Bt9CkVenp41FCH31d!1 zpobAIvuSLpUv0FC9(@Md6zgQX@5>^%9Izpfh-^nsp)$tfT6?2=#CE=QN`QR0_TuVlf`Bf;Q22QNTs55*CeQ*+;qU~~mh|Q2&-nfiHNi8cJ-s#AkBS`M_ zf%W!sh#Xv@4EDx`nQ6>~TS&jpHal?7<#HLp(LBfv9A+$B0r5Pp!k{2J0qLZn!xgqX230dtQU3pnj?$7Pp|NzkxOR^#mFeLHNYoJ^ zv6$->Xa<6`J)iREaAxqEU%f{`ss8e(WHpk^5__NS+`~Y7HM`ILlBfgGY&Jo#>DNh) z3;vwP0l8m%tQ6$&3M@^|pa*;63xOfIOft0wzfp}EZLJiC14XUx=Jj%Nd}6*Yf%ta5 z!4JQMc45Xre7k~xPwda179p(tgZFsC zRe%2=-UVdyCRY*yzL;d(&HT8fghH3g6-^WH9b;GM)gv48qLTL8MK;MT93)k~Nu+95 zN)gi(Mp3qa)%yerQVdIJSTMFzNsQHI3a#N`AD6M^R;HNud1<+EYm6zdCQ zZ{w%G>2`rfB*}lipP2vp{rq3F8x#yanSB)%vxRl{D(dk!CPm4{j`WI3Q@5$ZlH>;091mCA!D3)p{1{ z@e?_7Xa-6O&lekmSG=c~Tbc^<^yx9AQZ+-*CzD)aqy4s=w!o<+RfD1?y7+a%q||7Ua8wq`9kA z^?He4TALMDKqhM5Bvoe&U8=SmoY!m@imf7yx#s{yGSL%^vl9rQYj|^N)-Dsj^s|yB zvZ$jhq+dow;IsgD#HVaD`KnCL08{+|?i|f4P;e@f=oa>{E*$OXReu&aZPh1TN36IBcSUUCiuy&aEPT_CI8kswrJz)}?DJFSh<0SHzM63E1uQ`&rn2YANF}&qGRk%FR)jsP~ zUMjh5lLlO+sBq$0+g_?nD{i0{NTCii(kJJwo15w?oUFuAKiJF>T00KLB_mHhkFjo^ zmJtfT14`b_ixGa{*ldG6c|vqpApxB*)Yvbh-^li@X1RqAKeb7nl0V^;=FSluTMEuf zuotV});VE!cyh+1ga2BAt&H|?wcmcD#!@dYWw{qWyW)@sGliP6%AWL9!XKX# z{0p}XAw>eHN=f<#X!aC>%SJO|D}}qPqF&v{_8)Nw`(f6l4$(& zp%M|qpY2S$q7%en$c&TWCx-15NOU=1S;#0p5fsPqwPaw=G&6bJz&-}Oh!RMVLuu-z z9#E@4hWg3Uq;9YlAzKxkaf#1OoL{&){P1_O5hypp6jMTgHzL{i z$OSY~Aasnu(A_L}ur;0sbi2b>y>)>Esu)eRcHPF0s%IHGHK zOJ{h*6_kMhX!478Jg)6~!?MttY_eq~UB*j;!IH_@#B@VHHPmU5QFKis)Z4VmZQ_y{ zS>4D-M#rv#wM&=0`sl2e)Tfre#?thXtxU#0``4dTd6yWaYy~O(LvX-W#@;2eFo%mud&h;#}Q(P6XG#n!ly7;Cog@D@r$y6ZPX(QKt}ym1LGXK}9tD4U@x-BQJ1q!lRs z+C;8(W7g!RNh}M`if)O!@K-*|{A^H%Q*7ghY`IX9cq^dw(Vef0>PB(YrLWa#@X8P5 zRta7KiCn@U148rfY|=TD$+o$zPl=VcuYzB3yQY{Cw{A___?q(PjQ#j#r|`xNnuSDv|*Mv%lW{ZGbJdoXxM$ zaqI`vFV{U(#W{c0SLmlaVi(@pGvX)PC(k>-aH}WIyECa%)Jt^7>y*g{%TlW`q}$uV%;G*6@mdxt3Ch901yJ{S&lB2 zjBcQtHA@#`dq!g?YepAyXLrz0>dL4lr}Foo|Cv}p8F7ww9_Igl_|*SU*-!f8K=;FP@s_nHzOnZE0E&m3 zWFj`Mmw8RR$h&auxy`-Ixp;Y7i5CRR5GCcf8#$_Bt2N;cWA4ftwxy!gk}gb=!1DxU zLV5AWqNOE!iB3C~Di7&ecg1GC=p_@Jx~xj==7P{uRYt19xy0HPsi(iXPw=5M%tzJi zCeS;`J=~fGj@$M?em{Emam z3J2n&hfruRMH*lNX#${0$>=hE!BqUaqggULXBp^#W9S@gqZ-iMN#KR!(O~nHH#ny~ zK;o%XUbDSaPAo*JY^$q=@F5CU+%6T?LwIjI~;giZ~=@qbUC z$mD^&ea!m)TTQ)Q++B;`=BkB>iWkHH826z1wra*v=^}dJUGz|3cqv!D6t&egFYv-p9dDbFsj((7Vt+-72SL+oSn`3+j4^v>? z7bq(Q#at63ntKS7A7KW2Z_?Z3ny6?>{i>{UnRoA5R)BfEX?ekK+dglcW&|WXyh&VP zH?D7*WWB;H@1K%JT<(cM|DjUxfLx)ed?MFiNh|kY+WFN98DQ=W*g(BGU-V(u$MFoY zv}zQpMD;k7tlu|;52&VaH=Q1tz(O=|Wt7BV zG7$^dq!~;z1jdiSlTba>*mUV1R~!TCf9(VRODxHMN|LI%v5TqIKl0N*&Uy!wMq;~Ar7tPVID<4~WY$q% zn~B(!bW|a<{ zK^cZ{u8%GnRM1qo>^|g>RXlnAIRVna5&P@$`z#HsDod?UqBCsWcv}oHY&J_}MoHYS zcE-m8Gc3I&@l4g0L>sEoA=T2AN$^9cfveu1W?VlEPS#2*m)gdhdW$td+1j^HZPd@w zA%`lrzVJ6UI#E}yk`LVdGx$4)d@t8(TlJ15W{W~w4@wgFx+rSc8*L0lR6xmDvooP| z<1fUfm?=K9BMmvc0xDE|Idpne^5a;dSp`?~bsvrM)U&>kx6V#ai2J*>`-g1s7cW=V z*tpf1Jym}#ER-qg3+q_>Qsp*&Agy4(w4lS&ABE3fya-8ITeF9jUNR+4MIr{ubSeq= zP+lvTmh&^OSV$xVRDtr8#l%Vc467!e&Bf3|UfOD|E z;o(QG876uK9r@I`MXV~W8O}b^o2=!}wKH}xA{h#bY1a9D7W@674)0601@sYecJ2Ti zxHaZ1rIHxb9zqaFG5xBoJ8=|2Tf-NEGlV}AOdW8N*TVj=q~4ymQ9q>pgwpJ1S( zvT%f8K_Jj%Glh=x#{UuEp#``9=zfG5s~2q;2?jeIxf5*|7RYN*vy+RKq$Rbj&z`Q*7JIKkWh(Cfx7UF3 zN;nGSHBe)-VkdgAc#N@LHg< zONmx*HDlLxSwNqr6RjlPPa4y26+@UigE9U^x8@#vM7zQ6;NU#6t*&ZU9yGm=KVY75 zA?7idPQN3QE>it0Dp?`jO_xN?0!d9#M&tf@K(lrau>#h;*6~W zb$!`t)aEX{x;+z@x537kd}UE?AAjWX$I{9Nt9wWAbMww0*v+-br)HdbvjW^8&1Y={ zU3cyS z^Ga*n);(@MLWNHADIyjF{zCI|(g^6-2)dIHq$c#WA+r9uvs3*X+B`N}j{|sBiw!q& zsZzf3q*i$hJT@7%H**w$PX>%d&DFQraTki#zNdudWp{9i8K-Rh3&ooVDxz?T??Rz0S_1k(kAf3qu}~j zZiH3$Stoqu*U^6gf_}ye-Of)wf7#0f(p$3d2n>TLpi9@-0xR`Jd!IHJuYovRl>p8B zFLWph^ld$lX@P;^L$SwWk&!m~BkWz1POr!LA9hcw72No5o=>`Ei%T&#;Qbnq9JA+{ zj(;BAX$$_w8TFu1*ctS0qNa<=IJKBwC(GiAcUAyQ%D<($>MXNq;5sVvb2t2-z{5@ zkfA6WYgUo}8E;mEA&z_CC(Xp`-@q#sBanhX!d51Mh>VT@j68QKS||KYcBN8f3|GD_ zP3>0t(p$jhW5oap3lW>Kx$rC|ByQQl6b$`sd%;Q8BTjOU73u%=8D{KUwD@TnUuXuO z$p14)h5vjzzFH*{hW`uHkcop_2HoIMqb8x%yC9bVGmX_|mEaACd$K=GteDSlhErq+ zhAJF807l+0M~JlLNJ@_J*J2J)D$H#NI{lfnLd5IcuSa;A{N#S%n_Un=wopg0>7aU2bRzWuYjc|GC=7rz3imK+9k{ z2Pkc;lp!#Nv(}-N{s)|2gYk97wWM#^2A+rSf2DWU#6lm@gG!RfzxHI@|J|4)frx>rEPGEm1jM)7OBOjp7n#=3ii4FuY&cYI*|M;RB$Y zAU6^P=M{TpcL5lk&N#9r4_biy2xIO@I1RIEiuyD$@ci*M%Dn&-I1JPWxzU;A2XI4* zYxL!zo1nIWC}YwfnArpdGy1`fwg6vCj2QjGBWiEI1E^451>PG&-6n~EssJ4 z1u+YYjFqw@r9MYhWl!mPR!2b-I`b^Sk3~G#mGM%2MyJxIjv5DaHCfu#Gv^TQP3r2D z^zLK6g&N)%OnTd;%8Hys&1>qZ*#~=APe}ycSDUOkcGz(rT;lFi{Jyef-J(kaN8k@b zQ-mTI_jBErNxWz;igW%Syi;^3fT}*&s0!WWb79w9k&;2DmH|phMASyUBowOd0N) zsyrgw1&i~@&_0XdG@P!nva1udhqO>s`my^-y~oQ)%FiD;`BBI~2S*3Zmi4xWg;)&A za~jPZ1vfzXKE-f7Z6m*hYI*!RfTJQ!+h=)5$L;E1R}(HqAYgiY7LD&CKg6prk*x5X z6jzf?gvq!Qt7%2c087gc_I?uYmt|+X1gDOsfKuEn1Jjn!uq0)6`|MprIm-vsXnC`r z9(41KIzS={1R@F;6A>Y~X3-eS6N=tcn~X30USaofqdYW{D9@{8bq|KeTeV^}LUo)I4RP2VY-eY%+ingt6l8vUf+EEyHUv zk6$>4v$8T(3`xq$S8VZ7S)K>ph8B9> z!P92f6tM%%<4Gw}67%z_C~6yQGo7HemFLS5duedfELxe|X!{tz%IS`(Pxt)GZ>qB+C{ zV|*XuXuK_4M|Md*-G&?mL$?0D<{9OJ3lc9A3%Ci>3lv|wqrO39S=jGVZI z0A~so);e10Dg2RAB);>G{g~e(QQ!{s>X%79Ai&Z>T6$7pWvb2lLz1~1N8h*SNl z^S7u1qwAu+(t*C+5wPsOYC}!(cL$AqyE8}-+NCGvk%hsmY|YMyL)M*D(zizZn)u@3 z19}oN=I3fa1jDu2Vmh;7%R}xpLo=9HSmv)!P3SfPM73+*D0!`Kaog&o=+$rdJEe8N z$PR~x@11R0Aq)|&%>DNFCfa;qde1l(|4v(yAeOth0+m(Hf8D3guDx&J5y?N+nupmHJfAJ!`ESbgVGUcXH3$^JRq={6tSa1lR0|6P<*pL1ibNy!1 zP1xpKlah1xUr13+^<1ry%PD^HO&a*tbuZu7Y@EIK_&i&_EXU*C#*K`e%26wd(@YARLlL(Y@_R0?v*ngoQotd>5e6#Q6F zbeTOGDLt{;yW{T-2pK!Mj}v5~hEr^I4A}&!4>FkPLJL31On`X&-r`2`!8c;i}=75tf^Y96l+ zzn!RHf!>`Y4K}xa`ex@(p}JOkai>7ix?>!S`tmP@I~*5OUmk7M6JC@`ZKIrT*JFC-Ga1V7|<5k6SLl* z=l4nH(`&QsPW(46fFRL?_-AJA`h@|u;4SGWrXYh9Cmc(vkf!2u(Op^~TFyoY9loj= zjvrfirw!9pi!VHsC3Q&yR|)kzg)~ij!!-s}tKOZ|7OY&K7hXoNAJ%I8OBhqZuIHaE z`IWuj%O8qL=)d9^c>Y)I&)*@qYUW=55RXdKH~#cL{2>V%D6(=)!wtF7b}qs&(nw5T zq7jf3Ehb>h;3zJn(QM?)+8&%$$bFBW`3|6O^3v5FgVe~kqpDoIWz>79Q)Ol@Y}}1= zPeUv$d^?`q@-GNpe~%yvf@g@3-~8gd?~&RC$3VsE$Ccg`6Yf*Nn~)ZBmhFoQ=1i6{ zVvgHz=}i_xb|iPk1RYY)&@b;d5{zs}-#9Dw0fSjY{o2*2(mkVFv9uBk0@qkh|A8{- z<7R2=wp6;|(QU0DeCtjQeZXHdGz=y0AghudV?>`>T%fLrz)T@7D;NXB&EGQ=JG9K& zn0z?0?l?qETG6i_Y3f{qIqzm3q3SkLu%%o5Ww*vL~c8O?0L*?^h zJ@QRRW2aY7YAK`W(LM`Tcp+fdzChy49XahDxkOAqefKv=q(_rRZKM{48w@=|Os7?YPAFvQdzNtvQ{&Bixn81n^l*RW zWmkYwU%&>#4#Fmso_gI+&FT-WL>Q76S2ND?hy#!(tq&>kXwv z$+lG+=??K`6bS%!4G?VxuO`!|ZPQUS#+P$=6uASUWiVH;YG3&Y28J#g8Q9&5#_@rj z;!P&p2;{h23X#wpGbI{vc1brjQ6F67KjVSzlL^)08}X?(4aTNKc{UVn^P^wxK|m5# zKER;TSR6D)$E#bd7L|HT*`A~F7RuYYu25Q9s&z%AW0k&7tfHzb(#Ra8YMR`$7b5ud z==PoB7TU-jLB&SNk!)) z&$22tKe_X&wT5;f5_Ba)E%`}gCIaqz6Y+G+9=oukigqKQgo!XGh%hU<#TOGz)VRgy zGh`mP0J@Z*^-5blJUXR0kNG`Hm&21ZVdRenaIGFGI~q9(T|{@tAbQ`i61EV&IHQWrSuuA7z}^%#t$OmK`{A ziKUWoE*dA`E6py`%SZ8oC+R0sX!?WLWm8;+jmVsZi2N`ah8XBlf)6y1oujfG zZ%~kF{X~Z9m_iNm4o2xz@H|(Z*=GMS$9Imrg6!?xK^m9hIkz)vO0~nfc;Ui9RJep2 zvc>k$&8+lgefkl3GEohj+BEYTHAnEGUlZI9lGD#Gtp8%z(24f>DgRG7V#?gHr3AGN zQvVO9F$K_q9Hi!nnf_1bpbGLtzO~R74}Q_=dnS)cS2=<_O(ltfY|xo)7>!Y!p05QR zi)7g0-*G-bzaR=6ajD|vUf+O%XUClRq-EOG3GRq}J!bN5{SVV#ADXGkTW6Jb*+^JFCB;!)_@SnCAS ze%`2cm_|$h5Qk5xk{E3u5#Ni-A=r#-v+w8kT~*~@@;MlvJ6)Bkl^xuAdCZXjH#5O;wz;chC0LrQpoNlww?kDNv z{%Frag@l)Mmg`FaS_4!uH~PNY9*#L(6&xJa$?AHxox*O}v#;Lw3^}<`q5vUuPhFaf zGuU@n`l=vsPq(h$`AZUC z3Q!uNWUisbK`>!cK=o~Ac?w@rYcTb~tX_Vpl5)`gB+;)qM< z-_T;3!BrCwz&A$oVNu5aPO(~YBO?$6H4d5o+Bp1|TGl@ezN)*^A9Cz}-szSC=$7ei znB%Nb8)sK2iGqIZrI{USL+J_Cga}MDSQP?AA;AS5{(Gc$Lne9VE>!WS_+E z8I_4ouId#kdBlt;;@nNPavwOAQ=@ZPU4Q0W@&@w0_dJ1Uu?t$V?=K>O4n)z?5ULM) z1I=-a0JEzSU>%#Ez0gQ;9InhXR$F9akoIK7URRth2=vtggoKX<_>{CL)d^NIFxS%z z7!BQ%jPbXRZKWA8Wr=9$5s0g26X)WRCd&=A9#iPv;%;v37YwxOIU9m#iLJR2#vMPy zr;^ZWt$=>5gAiqCQI5JKNy^6o_M~gIFr00)X5Hu?1E?kduT61>qmLPu$vf5bB*I6v zw;`)Y%0fZNqWqd0J3p?I_R=d2W|Q2p;d*ohWT^BD3*?GdlMfn_`|MEJwY!AzhnN7Q z{BzjN62Zex$MkAwK2%ii(V(cRRu3`JOu#a^BcJwxupTQsdgaZHf#}Ei0<;R!c}krT zTNc3KaFUna1-ooy8j{VWZb%VW$#04!mLVo3&n$xUnglrhl>u}0QTrcx{+7ar6BGjd zBRFYtbf!5zYq6f)*)#)T(0MdLkV#$?s?3l<0d;X60HURcwFb<-vj&i2SG)oR^9 zLSTw9=2FrR=t3*{OUPpM7V)DYh&z%3Omv@yXaQq(fA0krXG~h0N{WjeTzGcWs?lsL z(L9t0{;VzH9*%{2LdEY^s}vH>nZ_r9?@*zShUS;vscebHcMs|;sA`GO^Ul_lay;wd zze(GZLrMZV0ti0#lh%qbhM-m|v|C325$X`Q?m}8ao&wDhOvxW}1WHNJ4*L0jxE|SS z?{$J}9yN^5H;r5oPn-NfCaQptiM-*r%?3nM=t(hVJQD%O84wnQ{o;C2?rh2Ux3>7V zJud<(y#%gDr#woSk+R$`QZu%|{r-nRN2xmWx4vywGXjg}-*fMlJC0U@!i+DF=N0n; zA#YBE5ZXnts$m?He=!rG$GH#;6Aw_oA!M#=JLA}Rrjp1N5Sr_aP^R>i72^w?kOSHM zJm9!_EJoG**&-=zoSv*NaRBs6GJClx7E;+16$c}f>sUN)4JhuMOdx3DH#W50Z{PPB zhhb9=9z&||*^@TGhFa;L|Kf?*g5XFfA z$hjFQ>O&7cf|m9!?IfNl2lY4uI>AiK=zf*ktU#@T6vlMU=vzSb=&XMIWXG&rLoGJ^ zG}3|ZCYn2Nf_$RF9Fo&(YR;pnf3!Aj*;`yoA&%H!*|wAd2jM*9*owE zmrkgwrt=0R~&&oR`#UoX04I(^3WtECWUNX-BVI;WaxHW+$GAa1Yi5Kg)>2 zalMXqr2}_`QQ#Yd;f{NDlneiWLWk~`*a+D!}|a|wUZ z5`!yme2|%$$Eu-oMoG}pbm(;CJBhDBpeMB3jP_DEAU&?az#+m^$IKaOy$WkOzdQ?? z^>G)S6q?yMvA|&&>xoJqvu}1%XE`~kY+m*_^ou~jt24J8liXKk-F=ghN(E1wlFXba zV!zs56b9M{k)iaA5(I=@T?Cq`tMx2uULxx1;u>!EJhzvhX-J01U(fxS^z6uuYYTAI zFPJ7VrlJnGQlvhlp}FC(j)nPW7k6cm&B~#&k;cS8^Q2X4?<%Mat*okD#lpes!qL!F zW+-3x;BmF6FV>3&UCyQ^RzQ0W&&Hl#pKfrLIaEj)XBh8>IpX)9iMi{hYSP_P6l1in z+ibgkKyIRN3&GJXG5_VI8F*rcE1gqrv9U=Of9wHeDJF2b$ihETSsJf(+h2N;Z=MGHW${Wa=NF95t`#7MWY` zEKxQ}i8v%2>{rMgNrvi8_HFAI{hLesm1|zG!V~~hL6>acV!6OgJen2e)Z<(O<&loe zd)P%;gx#G){Fj%!H`IoFB-!_%)u0psalbv7$nUP)1OR+I z*=!M>ES|Y|j3x??WmKhdk1Uq-j`0aiE@!}9$0N}?v`1R*ks0C9Ok9G1)NfWqbMStT z{GMRU;bZgGFYD-*5;`0)tf484@#1|p^H!$~$=oaDJ(ip~$|G;I(0#BGh;6QtfN(d; z+i~M{=qw8Geg}2($GRtCA1P@?*TPD96jXsoO6KDzo&~M$8o9^VrR1`knJ41S-=a!X zp`v3o=w>zAS3k$a6F!E71f~h1nDd(-m0xN6Wh4Vx*Hf7=E(UiMcZzuUYFQ!0H$v5cF-fX7J*L{Z4#n;zF z2K}W-`7qZ)5%k34B6gqB!==x*Rfz>qcN3BFKV63*8QMI^46V@60M>P^zv|VZtzc^< z5`SH`^qs;XmtAh} zw8)Ml{bss`mx^&euux*L)a*IgVt%pirwg<0dQTT_+`~z}WMJhQ?5Y=@4iUp3ptDUbwC{BdNEcaP zDOVSSAK2%{a;v@1Q}dQZx-5SH{tTY>UOE(U1!v8phs#2dd_zJ=UU;(3lW1zMhsE=F zdSNi1>4+R8O{w3UxQlSLpgveyE)hJq3qYI;pyo;-KD+~w7lHzXG#iZSTiGjL#54hxnkKO60qa7%qxONTK@ zbhY9L>Ql+_IgL)R2Fl|muvQY0O++H3iEE8yMgR^Kt4qq_^3Wru`0i)N_4Rdox~n`0 zLL)9~C+*t{pQ1f6Fm3!2jemz3LIbbMJ&t}tR@+0Vp7U!APA|PN<*OB*7TB)csagMM zrBBDibu*<<$jhyFmGchRC_$~gB_@5noTDV4$!9g5(^hM=F{s$vDI&?U)aP*IsGvHj z-X(W~TCnlexKNJ+^cFjZRVNrMD%;Qkm^v&g9^VelcTqU z-g1k~uCT!Q2S}ZOpPB#xc%-^VUN94sR^=sWYwiY7L?E=x3WG(84r1SQ(OvTy(H0oVu&s825`u3(cVohpN`%w^Ln>wE(}zonu6%}Q3?mF~w%t4f%+NvW+vw9+GRqd5nEqN#>o(&047@}%MXlUkgp4z$Nw zvfLqU{x)L2dAIA)8`%m0^|Aef{ZAZ&2HqAO^bG21#9Hfs@Wf8!_?@kYK`+^-q!O;* zu8%@b(8W_OIAO!6r?WI04(;E$k1FX8q-}e%J}Bg-_?1@P!dhngDw?StgTHR~e9IKm z3bJDjMdtDpL8$aO)K(>+%2u}!u5wZHCN&d_J(i!2=bR67m*;lH;wcxs49}UtOsb5n zS-?`>59=TEi5p(ua?yHX%o<>(OaiQ7g&s05EG|Ssh=hjI`clzGfbl+h=w#fx>Kh_&$i(W?LBNjw)Da zYyxrrQJ^9I>xrJL%WXt^4=4`Ae@~IG*{Di#()DA$G4`5W~ zidOdf_?svn$W`{WNw4f{dk*}C{5D`jT42fre@BKYPo@$EA0{7A;AO&`j^Pb z2>~)v36?fiTq5iTG*__)IwNwY%wF0yoK!B%dMK7bx!2_pYE)MmBY}=9Z?!?EZ)K9^ zNkW^kpRgpDzmO>Pi{!`+j?FVdQ|Ou{y(hzm)ep@xZzUzgLT{VhG;>sOTT&+rW77n&dH}ke5x-(c>AD_09UlWW?K#Qm$T+_xO}G z#02R3#JZCn&6WIh_lACa&Tax4yUw8h%{~8L-o1h3mH+D*|0l#!L-(((W|~2ya2Om7 zT$C3zy$*U8hH`cJ5)sWPE$TYCglO;>24n9SbnbQk!*;Ub8%|)*-Wr*FK`Gbd!|c}7 z#o8tBA@3m>Xr;N~`2jUUar5E&I2{~av$FwuW#36(APrq^2Z&dC;G~Ms+L$r6?E!@j zv=h(P*3zo|Zfv9u%!@Mh6=@wxa$x5Y^_gO=bdNXIa@8v$Ya4>sG^B;CYA&~qv!}bx za@u4OJy8$obi{@-1st|{T`Z1CF}Ia#bsVan0wQ0qm?>x#awDGD%1U)*J8VyAF$_~6 zaw88ZoE06y7gwf|CL*U%34#RS)6{(jyvHmxAUzNH21pz4QmnI0vI=s3tUXQ>#M3jI zrDfw3rw?Gcx?ecoEVBH#F5A?loEp`ouj`)Wyh}Pxsr>CDN(YmEYU_MN*0}8`=1uq0 zF5m#tn|$m3#pgmV$OAp} z$DfQSJo0%+M&ZH%@*756LdZ`rX%4t0Mw&LQMmQ}DI1IVI0lzC{B_Nsrn{|)gQ&^?+5+i2(!kR=_R5cQ^cU7r} zPww%v4j5tRBX%I$6q-$9Yh8yXK&*}T9=sUNK}Ig3Vr4lfsp14)6zVh|AGL$_s<0<_ zv}=W>9L0o6xNagN&V0a(VZbG+q|a9N>ZMZv_}G~UVU6~pgcXGqPb}*}!7EF~Y&MoV z<|5z)dno#~Hq6u4+-yj`KcUi|WC39Tx9={=3iHUWN)7XPM&tt*?sz^e+6ZT2>RL+l zMMlg|l2N^sa6LH!3g_dxah3HjYxQOGUa7j5zplne$y$1BK@WNe=KtA)*0grBk~8); zclpmEj*0-Yzi~H}cm)!RkzgO}J1;ONY#n0}`}GEOstIYgo@QPS~uD16N_9{GCBWrUbfyk zSIhJQ!=_vvWvAp7yjOkYj3M-MNX%@HuY(D-&dSX0E9zB%hE1xSAzF`AqsFZ6g6yge zQ&57LVIt@i%Y&{|dEhD>vBkqxxis58_UtpN+inC*7~-0!Cc`S%|86wKHc2z4Dr-@& zaZ9CJ0Y^Ev1&h9*@#H=*!6AG^@obG?Jt#V8=6q@~U^^?cv0oZ$bZUF6MFUeHf$brc zDMGf4K6QKmCct*&2@b4DX#w|vhyzSca)6{?bJI{iP@v2+2l-NC%$EHms5$k9T`S45 zSATz^j5T<&gLYN+2C64x1s#NKUdvdBEd4yES-WRP)2)t%;99^mu$C(#d92Z_B5G>GVrs`rkw-kRtM2EC=(h6wo?62J#QDUWIuqt z^U)`XqAt{UK33M>3DBn=%~O}ppa=@7pRlaU@ zBIy2ZK==f4%N-mux0(4K*YfgBz~r&Pf3P>SHvMJqn|du&m)*Wi!tO*&d9PKPlX+v> z@8cKE!B3_?5>{}G5zOfpCo)tRa!`)=?n5aHP$ zmn2*}N6Lf`a(ZzoX5(@s!;H}$BaE)Q)B51Bo+-6+%s%YsL4GRAI1HqZF-e*AM- zhVyjZs+zh^((!Kx_8gqDrVQx4?)~cr_`f`J`1=F=Kkt>9>z@~^wxP11gr*-pUsXZH z+5Ty_q(qA9z=oDp$NxwUPa^7JH1xL|EU=iO3??9qa2I0Q%h>M<;)QA?ccQqYd2;(9 z!PR40!1w&?@9$%mf`WlyqdV4CyQ*IFL0kkQ$OFNqW3Y52r*yK+`-NI@CSk-HYI&&> zz9pD|Y7n0?3rMdfdRFNZ!W#O6v(0^+sWd}F9z*YtU6wy7jNy{1u8oVS${Q#a8(mnt z?olz0BvS%g)@r3e-DSv3TiH_uimR-<#kVBR=bE(xE=4EU=qJ-F9NQVAKD1b-#hBjW z2UbNwi1Qz4c;wED!(|mj$^*yLJT;E>JK7GL2+LivQK9vpM_b$VDPspkrK!VD8fa`w z^;k~el`$|h<8SbpDyV|R7gxFfq*a-M44&NKtCC+*3bsr_^% z4rMS@8k4&fj^y`?_`;+h$`M4IA5b(%82B#I|iUcGB3kZ8dhYpLWf;-m&%% z-?8Q%@3{Yl`yAIt$H6ErsST#XW>M!VnYL?OWm1~2e=P+%OWZi!iJ0LIEu@|h+b;u6 zf)!<;(+iO;K3u>bSlK8yZ`D*++tL!RWvKo;CCmuxWH5o8p>$StTICTp&qBH}#9gFN zumd?=R!{sUun2k+Y)psgYNMSxPQ^OlEvM|U-T?5M-u~s_?mHUv`llkttz#NjE`FED z^Ka|<_w1b$)01Y1_w`c^GlT)EBiy~8AU5iW;9i$fpvprY`qvTf#*Mp(?#M!M9(a5b z4L9wTo=LJ!K!Di0*Z)EEtf|2# zc&bRoe0vnC{l%eEvXIHOEN$k2i!~$7{z(7#Uk0rG?j7_9koT1FkAnh%|J`1YjIpDm zJ`nRHU}$J;=Okid=-_Vm|8>@iRIMFRRnUBFZtKk3c8VJK!;|<0W*cOF2Ecq~r2^Md z3~xZIwb%gCH;FcMTx@d(9+%{PfBp!Z+>S&O#T$^De9FKZa&&FRO~)*bo;%?MLZ2M7 z9jDwUH{R~27`i`W4K?REb^~7bIQ*V%;I`YeG4>ruqMPlNdbpWr2lKXd{;F%D3C4$h zkRCwVo2f?^M234E%Ug_BP0r}YP15>$W>Y6OpnVk%ry#) zhpfAN6uy?PH(6~Uq4}m0=^hy57jsT32NNX{kPrU~(jY3_jMrGSfK9Km9Ao>&fVqlF z)7EEKjlOy4db*B`a9}^-nYj~Ppwg;DnbO*rmSTgGsC-vxbG*ybr|yvfsX;4?$iOI^ z+;JO{ykF}=&qio27tEc>Tz&HR%yy}+%rp8T+U6wHUUA^Was!t$#rfvsge_YXi z@k?bKiLEm_E~slmwn$f)aMu*1e%Eucwsu@vwQtS*ew;m50U$qRUuc~kGzLVV*E_gq zDf8y2F$Hi_BM=wuA|X^@^&*1itBuxF}oVRogkUwdh{dFqWJgYGn zW|2G)74FI1jMGs1W`pAF%GSirWRS6inZKNTZJ*>Io1u&*()Jf>@ZOqVh4PB}2=wNN z9`2n?qRu~8%fC|&I1@3ffjWsfvtPPINfqP0`QJI2_7T*S`b-`~C$stvDj9XXZc?kJ|#$QIpJeQGvuA+}E zX^sI2TzGwLx~|DZ1L) zW1C4|jLC#qiuD~AwrMPC$4P^3aH*#(8YtJsQ6hRW&CwMz(KUJ$n-wi2lxwI#+Tjv3P!>j$*;){Q^U8;Xr7B@wwW8JWWbhcF>`3G|42~tKyBv zG}(e9a7fCkG45@bLF%RYkwji7v)7cH7ZPoX@q8oJ4Ne#LCB$_}h4T~Zb-rlCy=qIIeuUB9-7pZgum#O}zL!bHq*ef#;!9JJk|i=i zD|KRNcURrzI*){TwZWyytG^HxOj+=*LvW<3;#OYQmqzLA3mUU%>oamPUF9R zJ2q!QxFzzgD3h_89h7a6b~x4{nf+^S;YqwV*bv=yBQmFZ6%6T+oaz)JZbiU|gCM+I zLA+H_aP)nPo=an(c*D}5MYe(d6kO=QI3oNe*MEd|54V`zxhuJYrEHs;9qq0_uE!XT zGBi&PQ2ibC@uc{vb^NuHB+qjNi5Gp(q_R+xJwjuX3}rG~9E(>D4uyVUmP2PT0;iso z)-p7Q zOGwp10fDP6fV~Fe@`FZyJMuDV`f9IiLCx|Q!Vq;=u7`4vbp6dXhLFiyGYglXRcEhb zbiN|R5`A5j)F-7?w-A#*uS&J?G;-;&{uipE6qyOqt~yCDSDYHp=|)nW$|#lX#8~3S7AT8LS91HnApQhy3k# zpfY!Mv!yM>9Z@vV=hVB|FG3FZg{>s^LB+ybpFzTAK*3PoAVc2b4H(B5(i2 z1ro}O8KDk#mr&Rcvw{U8UCdDg$OZXXA4+r5I0r$AJ)G(URj7m(0ls7qw~#_Qvlm!= z=1uDXnGjZL(D&{mRQ7%3#mpyUf6dW!?@=55UKVi_fH=qgv*-I?Qra2_T)Rx)X~z1)IhNNS_9hb_i+WO;8zSoB6n7Mmg1n|DL#JzN_b@p@q+p8n!jUTezi(u8NWfZE*2JHn@)NoBiJX@(rGIl?);>61HNAU>QF zx4bc7q&4T!ee=lF3!p>r{ff~D~qQk&^4O?M?cB<+op0SEnNKT;!E4@_9X-9J? zPzlntAzfCh6~6uZ{Ha0k_qsWw3a#UONtazmtyNw+rG-ezVoKVL(rJ^*UXiZy6_>p6 z=_EX|-klB&m*@<#sxwHhNdig_MdM7Qbd|X|ph-xS30Ow49$>`Tg7mD>dl`)NXR%#-alQlT%vSVVIfJRR^)|w}S z?)s#zm()|oFDo-e&i#RS}@vePQTAl59ld!ZP(%**`i~6J2iL_835> z>^j(TzMgtCj?)2>0u-3(B6uE&Py(_!Px`B}aH@b1uiRPqQdWT?T=^51(rX~ZOAVF! zGPnG@R`6#f0hs}vePuKw!yZ*4c{O{)N0oCwQpWQP+UKbS#}s<$WC}J}`jR;PmbaYj zNwiT}PPlakmF*5TaU)Yr#Fp9oPt{PJl4O;$4eK4~GZt(%u?JzfC>B~bb^PhvtLqla z;q1Bwg>j%{XSAb|78=AfH{pNGxwf(3))~xOu3NLGDy#^N(t^y#dRs1Ri^*^e^Nt*Q4~&(`J}r2l_dhqin}YV>>+b9KsH6 z8C}9ccSUIfWZLAIn|TWaLBK1*0Gdv>lwy4@oV*XxY7e|A_doQm(CQ(&nX?Bw?W4K| z_F&7+0qj5Yu1qNQz_0t&FK{HOE&)q$Y1-z1bFL_pP+rRd)Jx%z9f+IF;5#22Sp*F+ zoN8H{-Q=U*Lf2-!mv0aACw=onS`&A#CNET*S2LTxzjwtT@&tbUH@b_djF$PUonI!G4T=qNpapn03-TUP)guNSzZ^^;BX z;eQ=vCr+WdKmebzr~mkr{a*qT{~fpga=-mwuUTIuToquKU636b_gO)ZN(HCT42sk| zLWL?6;Ad^|i41H&&q*Tt?`u|%`kv(p{0-r^KMTemEhR@Z%j8oO-q5SdS6uM`bbP?$ zW>@E9rsq`Wm9@{uSqS0h@t-&EH)n)<`fz))P?&M3eH)CMLb`*JNEP~EKgjpIu?d5a z2eZB`4roIef(|gUU+1~KiMv^a?HDq75%0}?`38%+c*NXfyqtZ7jkeGlp zg3~sjl4V=I0F=>CUX=5w6^m>b6st!bz}LqgR*q5^x8YI>05tkgPqR82-0u~HCe z>+@)-&`EJT?IwVoPvIE%;tu1IHMW(dr?)8?;~M0;da2cujyIBIoC{=WtP_Vjo3{4S zN>wphhK+NKDLT=ce%GC2>-wTkob727nvj9Ae<5mf<3iF3Yb;75Q)Qu7>ujg5}Ou!R=UXGy5A)7IIb%EMK{ zDd>$hLar*J@$hP!y=w&Q=EmSpmTuZn9~HzR9S*cO2VyAnY*MwVM>76F7xG7HC?0ak z3;t=Gz9L~?hT)m#@4|xwB$q`?%RR_0&QX+pf4GdENIK(dqxLz+SLdoYB`wLI{I-Ei zK_7IDx_NtEwb7UD0dqm?2t@{G&{r0f5Wv>pg3`sY)U_1HqCd?yHdqf`P@Xejn&Tw_ z2_eq-hS(VtQ3XT4*l3ZKh7!)%X_QJ{wyn_rRDdHjkc?>2FgD7RFUeM_D4b*=U$k0Q zY=|DX6;Aa8f{xt|!+Ji_Qj;4l0*=jeE~NH7w|M(Ar60|dxhynOOpVtmH4ei>qD}JX zR}(a8Vj}$qK0S-VM~&dir%`j<*WMx%d}}s4k2qy|hbqY-?Ytp}IT{>WTQh%Dc}uop zRKvPIPD?F8i_NaX3J9?u*E+cn+|GXIGWLpEO;92&rEPhZcN^*|k7d>Ydg(Z(oLQg5 zj;r}@zlAQlOu(7$n&vdtD;N!b;t;SiNGWR>c*<$p;ghh~^7<QqDSE3{7qq6K=x6u?L3D|(Cz zmN13Hh1M>IyvZdx<>wG2egnmTmzSa{c8ogcQb+6;1bPe}+k;ko@rhZSSVZ;nI8l@( z+OhL0eBPNH+-S>;P`7)K-nU~cKX2D7Ph1V*)t>J_;ay@Y9hA& zOa$X6ZWAlYX1^6A^T$q_Q8Q(*YF(L<^RY0yMc~vPo!Yy`QnQ{B`+a}*J1b&C@0};( zd*LBSCpXo?n%fX~hfI3-jTmZ>g8*xY&XOtC`80_FBI{`q(%{8H z7aW_~qSREFXfb6pqtkLPgXbG_vbGQQnDyJ$YL)Y=?Vqh6Y@5pYI^b&*>mOgE|4VD{ z|BOd*b4Mpz2ls#1`hUQNSiJB=sJ&lO$Y{r^rD8>dT8!eHjX%%W*@pol*8lLYvly*X zM|5{x18DE+u}?{TjnNi_*s24e3Mk~w$;5p6JXe;rUMP8f_lC;xA&2fo{yHV}L@ceYN4>223th;JJxg0_O?HCm2b$1rrTMZTOMkH{$2Ocl@eziQ z?P|jfbPp_#t@$f-RVl^d+z39mF&bh&Ot3UF*o5}vVE|F)kwH`LiVO9aJR$Cv<)4*l zWDO4uX)`bx=|z^@;9c~%>8R~(6O0Nn`qx8{>lp0zRc$d5>rIV3%}12dzrZ&zik(SY zOPqu-=YV?yQYOwG0=>{al;n`*sLPLa$!YrzJS1)p(x*+JBS>WOMu3bDK`BYCOr!H^ z)R>*Q8XYdNH`Kqblm(kc{n(tV}Pc0Ld)i&iaGZy3)i@&uNdI^~|iW@sRE0OW6mL8ka{r)2HMHaLg`vyznvN|L7a|zTh zwFSEvx+?a#8!*h&b*V)p+gAoFuHZsT0HO^Pbd!cs4fYt#&68M;#N}=DW$bFT)31)X zn02#)XH+DHni#>aa0>Ry`y?~Zefgj$WW{kPb5KlCiK(LHsepWUC23)m{aCdh6@o+A z0bub9f#uOY}FuI*h#~_2I=O`Rn1y9bx@;JJk<3=TMQ6?4YhAkcnh{43+cq*-YvnmWt>z+zxeS0MVhf<5ijI8X99_{KbSHdauW z5q#^Rp)kuUL2VfQZd%Xm-vWC~5!?R-kO>x#H3%gF&J+fAshD5Dx*FQnJ)O5bLH-g) zqC(s~^TFG3VBI>UR7&(4^fZQs)#4XhTTF8&czi+hOKyRjM!Kx){1k1dy%-Jv(uZ?0 z9-JChkvt=pPB|WkX${dd+CX?h+q|Cl-LA4u;Kud3y!(8*Mx(##mCCN@n0`1Zc~8H2 z`-w2Vxb7{kMQJLfg&bZm6rYU;8ElKz@CGqg6I{zdA28kQ#+=wBx z94Rf6aQMN3g>!qu;7>`oPLTqA=pv#fw9Wx+kn~YTUG{MgQM;@q8Dl<@yK$3NVe$@g zIF_j|ORbcr;l@eB9I4_cvT`%>Bi^}I^Jv~uOl-ztM;Qq?Ch4lHR>Ar4>bg3B}3j=7}yle8B>=6SIi3VTS3S$1{?ijr9R- zBT=D}>LyTe<4wH@VHK1?v`frR+p4uoa&N(TU524K)i z=QtKSiq2{`OmKVVaGfpm{_NoWhHen;=#QPhGnJ+q&ZJ$g?*qywX_z93W%E8-udziL z3}oOhBUG=shn@)~?!sihswvIBCQUo_UKCWV0*lxQEhSX1dat@E=hqn?1$qk#={YUb zA_^)$4kK+)q4AX&n(wgUXOva1({g+8t1@%rW(WvB5Lstn1r5h}dx5`Py?&(IAuJer zu7<3)$d{zwk=tThmi?XA9p?)&FlK#9$j6fpdik8Od%pz z%}lbos128S2@)^Gc&qH2kz#y_FG1T1Vjx6({@gwwj=CT_)%$DE<8J#`yV+vj&Yndt zzMmKdacvZIc_uz>m9)DoBF6smgGscWR+x#~fkRdcb3vC&jBTfk6F$`@>dWGC3qIE? z5-j9}86yA>qxABh*MJ_0VbEWUVA$DdeR7m_H2(efM_bF3J9q=milP2-xkB(in-vS2 zI~oE#w*U9AxJtzmSg>LFB>$ISF9oB)WH;L=d40lI|)iAPSh*~;$6UDI2Pagp7AZunztECJn zVok0Ox^4=0Rj#BIpIV^4e=%{^^;cAxW;t5U{@$c+s+vI~&eoJxiUU(8Lw`tn=SaZml+Bm75V6EY$g0PReR$TJE_^@RnejCHK&8nnd(`4izr?HiyW7fi& zOr73g%24KVB11Q@bueJV?oqSX>74mmE~AOc1d35@qJ~EPYYa@AkzEXGiLp2`a5X3X^jS2WoOFP~NqKP{!o927rV zla1v>@a zIIQ~U+LG+d+i1^d&bS8?OkXYP@sULh61@TSjfM4LwP!93=#^XKBMTTq;%uQ zK{oMyn@i1@etbh&hD=6SlT`Y8qy+FqvBp$9)qa;&2Z3LKQ~h0hARXYmUaT8op%Z;6 z+;hODwV6bZ*Eygab7r#)K-txa=gb!BkuUjDZ#~D{QnBr`V8c-@6%zi_sdi~s#9O~v zMAhaPOp^zM4yFRjPxe?wY<+>PBLp7(%JS(G5FsJnH{_<$N8_;EPl1Eggcf-j#87SK z^sQCF2Fuk(n$5->YXBAo$dPy@3!7~B1CDsJejf#=A6uC6Pm_=?r8Clbpx{_X?N$1( zuZSjaP%T>6XJEnFe>Rog(UDiWCuoo6m=)6#I1N*Zy1+evJ!M1=m)37G%(zR^HsGF5 zIhoQ?`c*FD*VlUbe5ITOdDllj#FyfQzI1H(jsF8hXTCNN3uNGh`^8|Cpx7C(*oT#O zjlHTcfdiO(&(v^?x8?uBX$50T9%Rh|6^ zD{pibD|hHCVeB-gjh*8bd0Et3bL<|lFRFKnbQ%+!6ZaeDq*qS-O6m;Nh#evBqTTLO zm7QNUD33@5sCHt{?=sR0^-owcs=tVJZxH2s#)cJ?VT|{w=>6?K-gJEP^#=OE(ahFl z@+4l+NhCytB;pOXh-auInNf<+!W7{|BnDvO!>OfqJSc|?84Aa<;2c@vt=@ps<2e=u z4*&wYNLPZ&R(};zskCD+BynPi+(I5?v=YC+V46HovUg|CF^B8@dfhprMbu~$g1$~v zL8>@3NP%Ey-W{kH^`rs=|D>|c+o$(n>+xckGBxctW*&Hu1U5&`m*{&ei+ z&1{`)|6L_n|LGz5&_k~vVZbKJOVYv5E+XkFhBKfc!BaJr?TBomZ5_vV0*ds%ZgiH- z+rF&uZTtQLomBtVL=^MM@XrEE3Os4gXMBLC>lN>j`=tA1tLyF17T*WRcRbnSDci12 z93jNZ=4})Q2REcX$YGk)7y}{z6b3pi)e|EC>r$+i_9zKUwHBj~>sx#cp;Qb!rx$Su zuu{SpIAZF35$q`r2!Wfi`c_|8m)1V^(*e#}V+_c32zRbh-hqqjZ6bHz5^{83L&4~q z=rG2d3rKOBS#+7xToJE1WYLnHq*W76j!Y@sKohDQ7HKNvjJ`u4gM{YEY;@{9AJ+n} zYN%cTG^y-XIOOuyT64f9-2YkFYRDzjJzgNT9wFUDEmW3-l89brEpbOgEf%Zmt0C@nZrn+^*vfFH_ zJf2mn0d?b=FG_&1g?L(3VHHaAwGCj)y|?z%gK_Vuh89^kSOfsjFgng3*9nR@!I;~m zW;*z=9m=d!pWCG4j8x!Njm$3#3zq?RoBnB>9bqk%^G)w{Lb|NQpANkyUu{6;j*7B$ zI<5#5xv&s8U#?9zXlXdp)r|W!=v0G>dk*id~aIqLU3v<-vG>@mirE z(54|b=Ecg?tX8a2O%ZBw7iy7MDjHKNdR7KN6_JuASN00a53ojua6#iyROqb?kHmA) zFaII0dX0tl^S<7RvaiuEk+3@*;w~(#Hf+tXEnJ`wi~RQ)zF2&Y>KQ;w_o!uw;oefO z%6}=E^e&xI(rH)4W1&L#+Q?f@Dl=4--Z2473PSYAkYr~E5l0@l_4E2aNfqujn zK~Mf6)HgNL!Fa>Hs{5sGrVNkx7Y@Y{aRgt`)2u^yQfM?#cNvd#1Y%pYB*duEpvXf9lq2nq($;l>`wUz zQNu6VKo;{}h40YgMQj>8uj>)xL{=js{%2{(GEO1Sp&}2=t7pGQaV3r4ZsGFuqlb29 zFDN?j9;5_*gMlam??vlov4wgrEuiFxRm4zoVS0MkZ)|-r?ld^v6{)dG_EWkni1yZ ze3pj3A2b&}o|7xL-t9NQ^VvICzlJ|6+TLq2}C`$NrESB=4+s5S$*QTYU`yABpBVPhruNcY1FXmXWTTlYmsll5Ja> zOhyb^%kdffR&)a4Yq^D?5F14TcVq+F_E?R>^qf@g!No=j6z#4}l z{(x)Fv@s{JHsD9qV_3p7V{j7wny8_DcV!42g8}VfPT=RMOOb*H<=&#Eg$mCOYrjzZ z$DDXZH)2bK@rhg0NT}X*yFv-d-&=_}O&z8mZDuyHd1O`96<$-MN(4`|8YW(Ar4u+$@M=cNXv($X=oi+83uCoGVa_`H-$tX%l-Ph`j12=!j>50b1Rl_s z+>Y%nC2SVsi9d9wD*)#4ummEBeAac+PUhVb1(g_N<IjA$9OIaDZiIyO6K3E)9PyMCNG z3jwl|B(|tZ=+84^M=vE!{gkYX_PCaCoW7uYR5hvv&zmHi1Z6&OE9(f^&{I|jU6f&{ zwm35>6=ibuKjd;~;R^*3C;#cEJK5Kiw(n9uEzWb$9D{MJv{}E){_?|Dqi>Scm^B48 ztw|1sXCDdrI6=!nU#h?mdW#&k(UqQ2vkmB~{;ZwfEio-4Wy?DcInbJal*^$x|2Vg- zB3zc0-%NE%R%;{t62}D%vF(-vbPyDTT#7Ftq>4>`c(cRu%21BOyIjzn0tMd3 z&#(o|o0lben_zoDE08i&O zU+{F>0v~T*V9NZG2%FDQN_bxuGv_Dk0ob_944QNwY&dhOoWp3A4RKIbkJwxUVUNoc zFOFvPneO@1RS$D{GG0I1)vFsj4fOq6$Lh{#_Z9%-uydNYvKB6s170!6paC#Ihgp1AOS-`4Q$ za`*+y!Wk&gV;wCrd7Fqxc%j89QAZA~=tB41&xNoVDO*s-g zW~?KmF*@eJWY~g2QKR-3Ti;&{+d43?@TLKgY$`6jmP`XDMT?t+V;ZW=>52t_ZBm+q z(cLi~S{}wxVezbu)pER5Z6rS&F$yO#N*>NKR44N!k&Cec5a}Kov>CKiD+ehR1R8^U^;DdSV;ybO7qbOxu7DMACEcs?4ckLYkjYGBimlIIY+F$l zp(Grk=|`GYdvC-nslfI?MZKtsvp}GTH+QqfzKXtL7nq1lrW!boM5B0N7*u+@weQn4 zzWjanWgML(f8D9VQbL@vu8nJ)2ay?cZD{Di!R5S&VtGthuA1rjtPH^4Bj?Z}UzgW> zZOJ1-e=0iJ(}p~!V*?Bcl^r`>H7ze>w3tU3t|IfkAxQbDn{{<#aQYC|fm0Grmvtq=>YvDX}@cX-}RCB($(a;~;Inb%NU5ok~JJ z&AJp$z_=-oc|v|~q>fHjF`qsNkRE;3lBcgUB!lMEL6)NR$4vjEJY?E3VRy_Vy>!jl zHGNIiMZEUO-FxYRjk)z(tB==vYBX(W8pZEhHt}HFrH5oKg0OizWak`n)?cX-2f8RG z!8Tx7x>EN_onP^P8DTe{I0xA;gcG5j(adO40a>}pmB*h*3bPkNEqs`4{$}Zqqo1X*A(jEZml_fVz zTQ9P)gp;YPH<^v~>3OYqg*iz4V4xhKMDi>rbi&NUkZKpx=hPB=Qp^ILA=h|SV#n60 zKuX)C$yVHVw`|o7C8A)3A}Pc1uQ?RfiAHiS1cE2E?NB9%n@FL67XxCL~jTkF`#x6|E4oQ zMMUl10jP?n_r>H=X-Vx~# z&g+m~l@0nW8Us*kvlB#*N*DQ{7#Xhk8LIcg>_sf-iZziIC-EFj_a4y*N{D+lS??o;gRVSB zkZg&nq#mKV5{fs=6z-A^AJys!+4kFBl-onGQ!!bLclc)M8NWqQo*e7Hij8OA7jW%} z6)7ldVF4e;HQ-M;^iNMXMYExNHE!<@f7hEC!Gtr@Dcgp%P8crx>h*kZOIR@rfq4$YVR-c2kZOnjD$=pFtY^d0d% zcAPcV++_7m$@Hb_qh#(xMmK=e1E&MrZ-$*fveYaZ#O_s@qyyvym?zZ-sJX4oy3}c^ zOjV;fWVmdWF7^`#OEWc`kO6K(Kt+OgZ-NFRdJ_d|nK4<=Cz@{2@Fb&=jNJxlk(Km% zXf2Z1CT1HaVD#cJJt&%-GIl&@ItK_BTj;EM^&D)ETC6RNZMK-JhSF8a`B2RF+kodM z5JL3s2KYmVe077ZO!ti@Dj#WJWuFCkDp0e7!zu2LH;N*L7WT5r{zSr zpwK5MO*b?QsNd8a@h`2{E?YXJ8J%d)jD=9yB7(Ybe;7qa(AA^;z>IM|jiN$NkypIN zn5<~=69Cs9@E)H%ygpO^wb5^9vJguz1;wJUK$}59YpW-L7f<%C`36^#!IoKlk`&Ln zrMXF&o-D%kga>_HILZ>XvOzs@qZrV_>|70JZ+xnq_Nls7cm-_h@!CTS87Ep0A?Bw=TvPd*P z#$!hiE{IJPZI+6u1mmPw`&gY`;3h1&8=%_=wA!|##wh|PQEW-@y@A@Qb<`s$v?~rS zoT4@K8NOC5jNd7ax%OxVp+1xrmUwjWbxu87Y^24j;425{}T}rfwP8 z`@{Qf-yTTd?~rpVPGxt9H|-DUcXSwd#A1nX+Ld0-q@uowQL;VY?U??a4SHSGqP~jn zpUiAW{xb+YFi*7GQc~52=H_|;xwt*qnz=`byZ>fPIHNb$owo-mJXbS)tvuN!Y~@Xu z<$X%fHtXqTh`8a@972G7jz)tj-s0L3B0xM zXlW9y7wd|%7n(4}w`{BF&)xnpy!SO8mY&AO1q_)?(uR60(lv;4QWidfR|t|xeGQOq z8LUYcF(sKkUAJeBi_tF|E#2`}d&m89zD1(9S z0`_Z~Z`}b}1l<90T2Hpzo1-6pX@B|t+KvOj2kq8B*7yJ6gZ7W##L7y^$=uM=*x_G^ zS@Z{vC4%My_^+uEuK`oaUi>h~8aS2VE}Q=|m0@su^E;xv zTXyt%E*#q0zjiZqt><6%sQj07$??kZ3MgGV8_EGnmylnJ-(GESFqu+%88@Z(=#*K< zDQaUy@xHRrTDWmXp&iDz6de|^mb+0zxhg2-jEx$Kz*k6gq>x8GvQ?UwvXEk0BzbtZ zdnEX)U_)S7jU}zeXRMz?0ekRxFmk9+DVG}hp=k_ok{Sk2Xt!w`LBbu2 z_xuRhKpS1Rv2#q|hS*{nXu~;{zQmyyYBk}mibpFpoVZ3t@(CgWp-e8zeX_(q}#0}Vhq@E z|6)E@T(EE@<%H>4LXBsoLYfr__tu!qhIeIvims&QwvqFy4iUc=L18k3CrsTr<@*tz z9sQ-YT6nqgavd4om{~rh!`?Nmnb)7KQb_{i5jUR>el)qX^qW*RR-fTYDTsy5(ifj8 zhDZL=m5x6a%xCqQg=n5hAu3)J?|!@AL3iKA=l8~B!6eLI^O)y<+iu9xEH(4x0VDv* z8)h2XE?dF-~5Qa;#qEd+g*+(iQr+IVao8`olqtRZ42^iMM50gLn7l z_zc@3fV7V{%Gl5tYOVdaEwIf%5#9%@8viW~(oF!XG|19Pz5996_C?bVs8*!=-LY0s!xU}8jH0t2B zc=`E;9&lQ~nZ+%rNB{9xgh>JvVHT+t2$yb`r5sB?`-a7v?i89*3Bt@LR6e?_rjwRr7h3d2m^%vq1s1V9j8`M<|XFmc3G@y3`rF4Q( zV+s8cVJ4_#qu@N=T)m?9;lLcp7f$&wN6pm)g?$;C;g5H_^z29Q_#?uU28u92|B5hs zhfzp#7>3bw;la}2yp+OeKRyL^{!4@@YD!Vi6}<$S%lub_nazVPHps^TzE6_~gT@8T zi4SAHjw{s34A#k{p0_#)hCDu9O+4%Hd<~@a9q%j&`oMn$@rmlaJjl7HejVKU(|F{% z!wR4U29xRkADHidFJl0M>Ca^5-;X#IEhQ2aq~EfF%p>PthC$Bis&V_JT1YkeJ3j{y zMOZF=lKeD)OAxQ5&j_^gCM4Y>KJ60znsFukA*nxBb#r5s)ICyn&ccHI#mKdjcf8fb zcjMjpF>UMP?V93~64K1dhHck8lixGx%XM2c4+bk5N=p?9Km^{_tfhb?>=|rR*->Fg zr=zGKh^$dZ6^90xQ~s__z3L0{i$4ZqDHCrFZd9+?L1~zsF#qNeZpS7X9CH1rU8@my zVJ|e7z z5(3Qqlq}R#YO@7$!}{a*WQLD#{jMRAbju5`aF{VGn&zW>c4%XPSv6Q5vPf5b((jJ5 zBm9D^q-L6I0H5%Fp;E_++tBk|;k^a0~JBH2UkQd;O%Lp9S6 zQRDFmEUZC*e+1U-uWJ8CMwv$JwoVlC=$$lYDKb{F3*|uWYH?1173xqKzz#*aVav4a zoZwugT5z9Tvzdz3P@cTddSb^G(#rg`L+P?zV)w`1I04=W#V9VL9V#d7=1ps4c}(d=jDq5O?Y z_j%=Bm$JOlM^PMpKa08YoRXA zjJHr3v*dK0TN!@AMVL}DSPNM43bl_vDxdjWrYcq@y=^{tT+(6-?D4ggdA|MX`x>Bs z;CoWLCl(^vn)BP7KSm^-@>9mp576~XNXO|PU80t!}0<%s^T9r>OVZE1!k16+5hku{p+0e&+wT2MwY69jc%3P zpR}P73Va6XhbX8r2&L)(S!>Y14_sFU@ps6{KSlzdpV+YY+1U_hQfe z2sR0Yuk!kdohW3p(q_(BnrwnL@s9UZ(jbLMd6!se(TZEk=$D8g+Syd92+YYu8hBMl z9`II6=g{)cY`9_#PKk4lx(JNnHooYhq#-=J?gZ1{E!^iytD zW~5l){53YF2k?YESu>)n_R{eU8SGAv4`Gve-cwPFFCSd=^#;#Rn7;4>=_ftW%%SDKdd;73` zqih>}`wYrMVdmEeIqd31BW+HkMpVi5o<%J%&uU`&@poT6cnv50_yU_mtd{~N%)CnM zqC`uw;`NUzht7FxF;pPDZ#r&C3P6rPJ8NAlGK_-RZess1OULpRxImj-iZvJ;jtocj zq&`5Ai*a=;`(rmLN#ATE_8=OHMRA5UL#)!mQYupp`%*hTZAY@e<`&YUb(jt?q*3r) z6C-OwC?>Q7Jv)+?KxKF|0@*+!yOuPJ%zZGv(hxf)QTm!O-vBm;YFgQA9eo zwbD5@*6NDTrdiB~$e)m*vIo&D>V-FuZg4PTR)H0SU)EXU+JnmO?3|}^PWj0+y0-Y8 z7!5lgyEt!dt)ucn2@$fp!KtSn;xmF+J?56V2*I>Hu7!7_fyR1aPNKj|&&>{X^>?T{ z$!Q?H$Y*7<;b9D+q&2~6$u4UU_s%)NgnSR^=4EB-kS;h)N6ND{Zx5a+_e}rd&FVTJ zH-kaw4x#t9%JS6xC`fYY!lqoUYhM>vp=M6TlCPVu(d#Xw@l1BU{N&icK;a|w1P+qX zIZ744N8uwf)C@)LBQR9k(8X_diCQbV1dG&w!08wB?gYk$2RxueJbqjz6zm-ztpQ zt=5XyOwG#KFHE?DMM9$^vvWS0eYLM1QQKAblrNQ#y_ZePp1S_J;I?75D4l^99PB^J z9se@Z`sac()wQrT{_kX{qz=R;qr3-ckOp)U?j?aRC#uDN5(Pp(|J-kpumwoGfN$7w z0_Ys6p-jPIZ?J#JF7OUCzO-#g$z%)H{XE-s(zMAhqcHIZeqZl=>mE+)9^UVt?zzcc zpBEl@J~jKgZ9Z%O6)HW12Tlf`c9r#SBmU&jDhV|xd<@VS@P;1v`s`t}tI;{cyJ9G1 zD&gA#9>m-Wf_+3a9@yPvvcp$^hzSD=k&s>JB{@?f9y-2Z@x$@dpfN@ND!?;sJOWEo zqs0J8fZ4HDC%@c+8K$RjS;T?ETw@?ZjEI~Cy|EH`L6c6BqNilTcSm_+D!p8;&Wa@@ zt8WrzXn<3$s{pO44aI;M3}C$_HAScg zceX}6zBTp&+WKYKS8JC!&2sv5+0U)_sOm;enQ(%FKg3X+fZDSV6O(Ix=ZN^Ko|)yd zfuu&USP0no{iDORLkDve$Mvw)vLx8D6bsawC11&UZMs6wB@aGQDjHD{ZNn9X)p>QT z%@M6_!>fyI1?TDTW5#n$wX%_8<&-FigPeUBE_)#e48it!RW9&R1J7?zveny@gUEnmv~QLQDd{I)$2^Y5H1}FF{4w1cxpwfeQ8ktQVs%uCsu^0bn_drEsE;u$ zB|;L+aMvW}O&;Y9TpV4E`RC5gYQ%!#h>`6`x@~NPn#-BTAriPcAbn--7P^iYaG`^R|pyeda$wK5r1SsxU!Crlh^%q@dRD~17~7W30uGe{5(h9cO?q`@hvMUwxFOivFLf zKSTc1TGHLdsUchcN=_P3UC#+fMtZ8dsHcWIY=&*_pLt6$g1xcKWz61N1mI8nA}4$( z0*i+qgUQZI&+O(i$;89G_v~`T-?pDXTE8HAVpIRc^mFZWK&+s_N80vJ11FDd_8#&0 zuACRJ-MjTcL=NwaSBxoZgw$84DNkKb-^x28@{YA&7t?D5V5615GJzJ_I2=TzT&)*{ zYJ`3d1xdJQ!4Or1sew(VSSq188OQt2>$gx?H;`WoI|XtQYh$FB zBv-FNz67z^EasN+P5YSNQGMNM6o?CGTJ{5*0B@VYrFOxsZnNy>H};oog7}&VVF7OK zUNP=g!@#%mXnz}q?8ZoJVBVUNO)Er`QP{En-tO<*(FZRAp3#@EmxnH+49>NrV7%)1 zBX%(}GKCdu4B^%ytCh*~EdR&L@ZZ1ogd8m_{!FER3%`ngTLlT0h&8F@wA4zN6HB3D3PK23 zP+%!4rhqePxV4LDrS1u^o!px8u1xz4^c{j{5+g7w^J*`i>7DfV)#{odD1l?C*Pti+ zL&gF71N(s$a7_jTr;O&HQFz!t-4F-aF!}OP6JL?8Ps>E;GlY_hVOp80h!Ogx+pa3z zWD>6nv4=WeRxT?k^wnZ2ZL3Hk4Y*^jFH{d8&?$z^9Xmatfl@KuCjPx9vw#Xxu}mJZ z%OqGrs5P)#P)rx$5a{e6X_KM_MQs}xlRQWF_>FR6mc!Iy-V9@px?;KuET!t*a(7+- zejW8#OQLbKzd&XFZl~HzYYq7j71N|%q)Ipd!Z3lAMDPugQghsb(PZT}K%?ZVGD5nk z2hDuINEl~t=<^u~c}?|5>Bk+@PJ(WBOu1IRS7T5BH4OKl8$`l?n;*fk+bVw70_ zT{U`8tDKA#GhUqTDc++9t8ff?zNUwg^Lc2R-BFpC){9;$D;EK?g6MPM+n-+Y^Pw0S zN5}w?<}=6JPVMSWnKZ$+cb_de^`yBZ3$hAgoO7=~E3qE#(a|pDWXSDlQsG`RdYB?6 zZoi=lq4g#t`MnRNvwAnKmxVcc$z_Gepnk&<0!g&C0P3$T<_ibem0)Pl9C9iPwopWv zEDjcWQP1?F9?azkz5TG_inPE%H2$waG@LR79=(zamZ(>xSS6gMxBP3cZi~ena1i|( zJW27CLtzIfd?QsA8wxbFTIkyDC zYAK0tV;Z$nSQG1Gn4y4N{R>+fEzc9G`LSQtO83)Po70eib`NG@c39>Qz- z2+VtFKk^2*ZgC$#Q7#ke2TkFWxV^32F`}DtZCCyUL!LTl4UtKgfw#abXJ1_xp;u0M zbnGGrzuo34!R;x*Gu#zS1)QxGyL*#dJudcJc;Zm0S09mL@+p}F4{|YD1d@5h7?l)Nm<3S2A}#{OQwM}} z8H4!f$qkGw$X;ZJ8VnB3)VnelnopFI*jGM~drR`J1+V_{yq)}^yno!wd_eayD8!n3 zewT>Z7#K!7pORpKq=XdCb`Kv!7uvP??(vOzvu#k+Jssc&a-CjVSL@=?2sEsMIvca`?FlK=j@&DL*1jVULNJxp+8ggGBrceav`3DzeE{ z58mYvE1u`W=Y`rp5_1afmL%^Uo^899iPWJ2Nx72en7WD6x{3Guy%nY>Aalg~vkhkS zO4r-_ga$}Q%F!>r(c!%yjs0E5>|c@|<+=n>(nujYNkP)g9!Wk?schWIb_~8N^vEjw zeB5Yzu(ZjBup|^0eYzxr3!1PG-xDylcb;mNmIQa8L7C7we&ehl7t!pG*(Ealq|9i8 z0krQV+4&{A4lyg+8TAjZ4Mx+r25ADqOc^l@L)b?~aKFJ!q1w-p$5E?Op>dIkX+$JT zPQ(x2-{N=w9LOYey%^>MtNUTyn$)7O&$`QRydAa2kYOI=@bpIc5FotB^zIbro~63I zu1H&{nq{-fj4>!U(%455gnXnNWQjho`6ICP@BlzF9~VLcuG)tNg!rJf{HB<$y$hMJ zsp1dJGqEnbaUO#(8n?3>J}I|pX<=+dRY`zKrlfi-MaQv#Gb0h{jjl{ekm#2`M|V@p zllcXJc^q`#n>jUXn*s(u2zobMtnv0-H0wtd;R;9ApUyUwJT>yKm! zbUSYuG!njd2ZHTyQdT<}+E|4G??P*`}!a>6Pgv9WwB zHj3_&q<2cWTI09!V_Gg<0A*lR#rsY1Xng;k{0N6#PG#nsG#yXrxl@Ow>n@XEM?z?} zFea?Wv#jnxfMSu~$*oHH#<3`?y~=ZFY9e$Klx;yGUp(a@=9Q!LTS*XNl_zJ2l)8}+ z#h%R{BTBp8a+Kv%(_U)}f5L^wp_<xZa!r8>C_d8U5RjBN)|w`mj2e za6b;=y z&rX&I>LBq_4Va6dwPhGI(^F-fQ!cS0#8ZTZqdgtZ;itVMovE|#0d;>tXAq}C5)}*g z>d<+enoOUL=(5jM5t~+G_ZEj3_rrE^r%9>cjHHJ7D+-4!g6%Cr#_6KEoBp3RNo$1T z-0<^LUO>3)_@$nCg*q&X!!(P1$frAUQ8%GS7F@~Ib zAY=+nS#JJ{2lt@qTd?8!wLm`?z*7-)oc|w-AYWe?G!4*%;`xszlz;zB{I7=bpJ(D< zT|+=Ql@$0)T!WO$X>4$!FzXeMyVleG1n||11TXm_QywP3Sft)-wVJ7L{8zvE|EQ_E z?C-?UzjreKyb~+#_5_TKjL$u@+_T+{uUE2pULkdm2syluoKyLEtVpMVS>?!ew0ikL zn&d)sWqZ>xt%_s%#=fk}*)s$-T}eTcPy;3m_iFNjIHB6p2_qC?4Pnh;LIdS;HP$KD z^>hS-Zv8A<;?(W@7t;L*^pr4Y6D*38lJjWI;5LRv+`K9jh>>Mu8<~1zN}*b04l31& zcT9{@Y+BJyDO(`I=I%VLD;udsRo|PAcQs7DsHAP%o>a^#(&w(VTc0Hz~YEslDCle06 zC>n*+-st5}jYv1U^H z^%9b`3#PeBaVhn=(occ31WJJjv4T>22Bufm{)m=N!&Bdl&qp#%u5HgTTNdJViA5HL z`SrMm9(|LZKfUmbuCv9Zzd)JB=bFv|ThWl=bH^*x);CCsroCy7Y&D76lxOM3W2n=a z!$*n)(x{zmRFST;G&4Q--&4}37(!rijBTZQ-8t5Of4?(De-%^V4*E7vqo^dXECiG< z=reKxQTs@H_l$`R*=A^$JpLnJ6#pe()C)KWa9f0A)HJW;Xv#8&kT?&<5hbk>h@Yd6 zq{!Co)#vVjuFhTNMnzxN1kvDRZw5u3m~Af3%lZP%j}dAoVepcY5)vN6Rgy<87*+Hd zZ_adJP!9zhjz6x;&bf4F&FLDj9xHyPRfyL~ZE|=jscXHZ#=n)=Y`0G#pfMP6Vq`2r zL-Thm%y4k*iePuv&reP?atq%!w`Q!N_U>F zc|C8fbXQ+@t#-`o{~TKr_IBe9pdI=&?o+z&IprPF%2F>i{rp3#;0nYOc6!t#>b}<6 zBoA}C5-Vj(yL42(D2DQkhM&mY8>a%BXB3YW974n?nBdGxI& zv=LEU{0nyX$_02tjZ0WOKKJtX-~Q+qFHA`8W)BQ;=@24!S4Xol@)`)`d&x-;V5v#q7x7i(2e* z{(M;e85)=u5uCRRBD;+P78)GeFUqYQBPs#r7Xi^JD#-E-$#r%)b-QXxoal-{(xeIC2piX&uNelc%UE<58&`BC6c^o7-V!|F6 zefzI|K_7KC`LOU#-be4#0(YB_AMC6TL|Q}d=ey7aL9Q#yC{!Z|z}U-D54%zX1wY~k z87Ax=gx#tn_Nt-a4M6S?ft^5e8-?tLGZjt`ykdi<1M3ted(qMe=IfVfi!D3#p*t)v zVLJLP&q9;ROof=0k&>(w$S``;(>nSQ%IspROdGkhbs=HNpeQm}sWjYD4J#UpfLey? z4%Htmqi_YLDZEH=3`%Q9QG8{{)RD{hiq5%(=5$rec1e@gW@Wx~wR1u+l+ksRCVg9| z=r26TLa=m^<8+*c{^6p@{B-BmzGr2Rr4(T9w(5xyXjuJG_%PTzJGR;`yHVH-^v01! z8&jDj5Y>=8W~E4El^gsXNtYV4SQeIV!eI+~|3wZXwj^kX{u&(9SJxlfC;r(uiKWR^ z6*n|FK*_5;XAWl@^+%o(>iuv#tpjCZZv3;-rRouu6!=~947XbekB^P0^$W5x12iAE z&7k7x0Ofi74|Am~Mhy$3_Ij$c2JsBvO`4!eq7FOVfe+7Ya$bcLt7Vw}kCnq6i>9;4 z;Vp2}iUMU=$y2qTA_Tt-eStahm4X|SEj!VUEvZCHenM@e5zc%>zuUJ zQ%b;@gAt1XN%Jq2)5e8Is8h#| zRtR>ahf zP`>!Qo%$!sMyrOCqmJtomIzg#!OP=|K|C~&p@>Q3KGEpkx_dVq8nl^r)@hq@8|Ejg$I6kZbARzbDHxw7Igt;j#}DA zzy%H`O@p_x*6c5UDc=d#^R?Hjg^8|D%`jDOZJa`kpP z!XIP5hG774maf1DT4*|!AHhE47NbV_qE!ceAjL7QHl59H^3|#Y8e@TR6-DQf*gPe* z9lJO_^@H>o4LOrqT7#k8EaggudSm+lT&gAJv?IWxq7|*}!e&`fa}{`6ym`=XnI9=b zsM0I}WL#n`2tUT5CUzXRXdt!1p;MTDn6{}a2)wrHVqAmw%G`%ICfkd{;81lNAyZA^ zO3en4l}OAyDp5mD5ZDd6bE>=cWncU0e)A|7Jc91zesZHVm7~;?50hk1HD_QoTq&ZQ zzsbj#rr`^nGW*nH^Nh!c&|iuF4z5<7s_jW!Y9?wXdwsmae*TzYzByZ%I#y~^z{i=d zFk9=+4bft8en|6hpLZ?1w^ICKiqB9O-y-sTy)#*5{z9D6tUEz-MUQPR$J?cTIn|aE`pJ2FobG7Lve0o@`U| zs!eFx6&is(f8|ocNwDbIKta@v%A75@^)XxcXJ8tGIYDG!w)-X)8~qtiMT``~)EN8R zjc_N3jCV^0*0TbcA;ZGR(5#o;`AR-*?V_mgCMhC&~N5yuQTuIXAlU<{BKt!3zMJ^MF zd=N!MJ(iX{EGHWYntRqV=s^e{+0TN&j4xeI2x7!^ zDI2NmFLYG5(DqEAuG@jRewMev5JobM^%lL;VKd@SAb|6fJ=wX|Co9}Hg{lDysu0Ce zYnz=7&g|*ST1p-D;Aw_Vn&)&Xjb?5oLbSxZ%x_;iQ+1cCSo<08m4h2{DH~Ehx-1j6 ze@70IUt@~)sG`mUf<&?lbjLdTQ8mWjAt_@sY= zzNi0qQU1%v;Gc`~f8CFTN+(Kxv|jKy2}oo>{Vl&bu$3MG-?UIk%?y=%*#yX%QEM*{ zuoEeUWjY!a{d!k<*Xr?kqZ`lhFS&P~r!`4>Q~vP_Q0~pZAdL|>zZ1UawjbwySxL5@ z*nQ~oeuer)-)Z%B6V2HrWCNMJY=dA8X{#C{NU$EUNz-1jok?$p7+@B|SlPZP61$BCc#4bvs6*mL9s8p%TB%u+B6Ja!A#Y z5x5=b@99Ti*S}MTphQ&_5GWYAWv6k3(Hlvhp&00OQ6Wp1%=hl5{j9K_6fH z9>|P~i;P(sqKutMm^%BiM-b9tG%Zov4=ZqnaU>6F%~&6s_KJxg*k@Q(M4a~>koYBM zDLt8ZdsjO+W8jT2O zo>+7mti90_P#}=@;V`guB|7k^%5LOGMhs`?NOJcdc9zC<>Ppd2wi#h;#we1R$`+*_ zMcs)M1668)Owod?b@`a!vQr2G8oe`zQ?-s^zs+5bfl>8F$$f7Ah^jbk#o1~ zJ#x3iJqkCg;VqOmTstCAg@mMbrOjN>dZDG>o0Q2E+f^rkRC$=V8Nv250JDv$Xkh1H zWZaG?W@%F^(%mYW7r$G}s02T@nSo5xo@1;_dzo8(AmaM-EkeK3Xxu7_fO?&vRv+pb z5Us#1Ir9u1$$y(YB!8YZQk=Di5Gc7(q46zm}Vc+HiZ@F44*5gpq0-&U%8>^p+*A$T@N>idi>1Rs)gJIl|3FO)q0nW01O!7a@w#Q|dJhkrgX z@+}HkMU2&%LMKsSPIke*6+!6d7S<4giC1X8FQU$5tW$B=D#GU1lN=yJy(f^QZ6GI* zo)_-yTWW4veUv0yeUy>}4TEzGY7N74w$OCE2j7t$NGCgU4?Z2$_;$O?6bE|=+;aCM zbFQ!;KDi!#tM?ASoCK}kKy7=-)m;Bse-jGp0As1M0G?Ga2hI^#QyvLPdBkz+kC{)O z9nTlJMVv)eh9Jb?Cyz2~x@;*hqP>`}SC3;BoeBka}<42IO3_hc;%3H%MI zGbFkFD@6|iAlTrkbZ`0O9EhGLCv4)}@oH-H2Qzz71WW(b!cpx%TNLbnCvp%E+wC{j ztz`v+_R6YLwdn!O#MxKFXjy(ib7NDOR;ldS2hzZhv9#1j7q|l7LT!N4mytxNmn0+7IJZ z8ynuAM0Nl?H^*ZW^aj*}kh%;JBn1_^>;V!fPPk$&#Ja#5B)ZDv5zathyV+|%NKq-9F9L+C2#wL!*!6OCQw{_kHMmhi%nrKGj0 zVqbV=t0B@4&LBr9h%($S&U z5lF${_Gg1A=h2JwlI0?-k*jd161y>3*=wd|;XUHhC5MyH6ZQJJ_IO1BZl#kZjXzb< zHI^ovy6gS%!Yp2)Mg59kyXjX^Q~63cL}2uj0|+d&ag|)NCoYH{0}UFDB&%xFbNN3lnN*92komA(XeRfn?G z1;py4ZSZg3*K_8+k3|@Le0mW<<|6+tltBZdH@J>95aR9`n&a_379nX2zp-(O7ONDrc>?027}*xRzK5t`_Y>L9mS28PW2PH~r03#^BW zEVss*Z$U#RF7X_R5iXIMcSz_jND)rOIx~Moh`-YsXvMj@S1W|1SF=l#SEwGV`GI^IO9_0EKjb)YD3MIp|+M3plp zA*SmLU}xHmSQ$%v{wq#JvZ?d*30NZd?H@UJ|FRQJ&QRab)XDI_fPnv|7l2)Y zz~jahA7jBn>$ggrGuSUqE7D934oWVB*GQ>Q3VAJN83(AICB}i1XbykqJKX>kVTO#FdPK-2ZEK2*X~op6S|<2 zJhTP~BQe6nYIqf^{7}edqEFPNH=ei5o$L2C`pzy;Qq(XDEl@u!I6i5y!r*uHg$rje z71fO)j&&r>Hb=EpiSe|=H-d&p?Sno zz0A%%I=RKGhsyhuTEIoV>O?R7a@%a%=Tsni+$3-%gAwytz4P}Ocx+L5pv$`o=hSp- zs?teh5jk|Yd4mNjl&*|fsoZ7KAnEfeK;Z}7*BLv=MN7z_tIk0fxg|lplulyUqlY}j zeb`)NStu-G&ZCx-d8C>*{lh**&q=!TR8`kr1Wu~*NfhHJ97)n=Z3Y-elii;3w+`+E zd_oDgl7%uD{i}d*9g{n=l@ z;r&9%)p1Jn-}+$6xgz-b$P8fWKp62CX^egI^>!LnF3{%cv?;N@X*Z#6Vm+88Z*-%e zq42|*?=Q!wuMDOiBO-69>-5SRNbiM{ust}0bYg+w{ldJaKzl zq%WdhGeR7IT`efV;DC=gKzlRteEy32CUcGK*8vvr3bOwSlGF~;<2mGvl=3i0x7jG{ z)gPXiX#kK9aO*YX(I9Z?Jp%gx^x%eEkVBr#XX z4R;}f7l-Q)d(9DAU|@Fjm9Qf+=JgdxPRk1_{NQ@SdSZV1uc1YUL=5ofxQ+f}1>wJ> z6aDwN$vPT3{!J&UPypgv`B1nrd4odvVkAL^329Yhs?opr0gsv-DzvILKy*4Nmz2r2 z$bahNY7C;nF1LOd{xYBXf?5gE|bgU`O64>kH}CA z1Kt1#+L`VUF77Qp4(Uy8KroULx?AnjSVu?xDie5;{^1yAD+!7wHl}1wHz=YziIj2~|H?G(6NZ{{q8$7BLs-CfJ zdaT|#QbB23ltQQXguJI93J{_!RyNm@PSF4us8K*hg2>1DEeMjXZ&6zx?g?n21ewZL zW8=Z};%Fil=bePqkrvGJYsHo49bR72k<(Z~CPYnV zX(sg5ju=Nkd2E3={>13$f+X(~S5HoOsg@CXSidHGT3)hzWg_9;NBHeZATmD-|-R2CZB1C-I~a&J-lBE|K4FmmYf6D-N4+ z3#;x_2Q|@e&?6&=5ro6l*Hs;(^lpgu$A;R?mR-UbJP`reCc!POar+Sbb0wP3rs`(n ziD7rk3SbjQvvK-0tNofpOEL$`CI{R7_9;clgc4O^pJX-ozRV_%M|p{=i>r;)p9}Cr zE|{=NGUc>U#@OB-ivyyK5!F44>5=HP%n(mL(Q87)X4%9Y@#KbhX0_T96% zY2WKV|KhyiH6ej#ZulS1+`n`k$r;*M+d0VF=vw}7dHYXV*os;%=Ux!2z|2KdPORpd z5S`r2%q8|ym^B-*eyih>rZZXMGuG=a>xVzyF*`KZJ$6O*kKc6fpFYC=Qn-B)%H3XL zQkPPJ^7e&&cIxZ%0Xz=~x&Y0+`h{{=@l^N{irb|cCdsI2iuAS;rY*IJi)JsDX#stD zU%DA>u`QN$p=@{FvxXe*2&Ph;3#TMx8D=TwJbK(sqFh6(VrM^aftu>Na!uT{3(lJE zmpg;lV|ZA#Xq8VMNP$VaL1Fisyi!qYMQh0}%`Q-)2z?rs;6U=+b&o@UnpMOk^TsKY zx0T8%G97D``LU5=ewNqxI%equUoJ9H= zSwQY|7|_jPY~N^EI{RZIJ-3O5++XP{FFNg;nVRDoP!+tsseAY+!Sl}9 zzM)7K+Hn5$nv_j^n#XW>I*CsRt!g@!4+ZBty)r(7p zo#gW1=J+S91mEBbz0tY#CMNyK43lUSU8z1F7pG2PpBQaS{(PM-flzPuEALu}J`lJ$ z`7+06?`DFBBhb4+bZ@yk_bnqp%Z!=#GwP4f!108SpGrslf^3y9bf5*ai(Oqc5p!pke0<3epIE~&QV;7q$4X~qBUsy)0oee1&w2wO^ds!IF@%7zYsB^Hp@=t^Z~H@5{>nIBsq?Nz{x+QnjEst58lAf+E%u z7?`-t4kjkcSFVHEs1u~3*8r}JV70hPsf2<}m#V}}L5T9 zHI4I79W6;Yk?SZ+%hzSE!dKhNlhJRa>G$0w_xrF^I2omc zigU=R>N^uu`Je)v65H8PdC|uH&1xmGILhgs!4YQLP;G0fV>=CGc-`Glhfki6%g?e+ zEIqq=0eY>|b~zP&{vd14N)(uS!5p2DN?|a8XrKH`8Yu_3#;H{Gj)4(5l16-$r)TOi zP7)binJFWd;nm!@h&F&>kQBzLRe)8SsqOaFtN675>srtWLA7i4E3W$kWePP% zX@MO#S#o}DH-BRr49Obfz$^dW*l>}bOPz>4?9{HzF|Jux;pVSOw>B%%ckc=);J_d^ z8(He-l{bDRqc6mKA_fJxt>>z5O3}ok8`&a8)#amYoYg z04CqdK>Qv1cXiMKLtb9x7P!%o%Ol0;Q)d3=U>*S{C9sMu^N%#XJ`_7nZB|6)qh!;Z zSnnZFrO39F9QG&dBDVUFfspQ)B|2&`D>_UC#Xoq8ZvaV9ho}1kD2uC#jReU|P1+zg zDOi{+{uJ$Xw`-1^_zvtz{q7OU2_3~%l*{Z9SjFt&Z%y&r@giLO&iUVfn04=C=~~^FD9V+yTO*zWjRyc-)J+>N8ZGTP9XO zidAv7^sZvua${3VgFoVuD_cp&JvNRo`GiS$NV^E~pKdnz5wJ$7OuRP3BR^K6Y!N4J zuFQ+jkex!482@gAn!2q0+&$L7cBkM&vE%Y`F)5sE;pL6b*A=I>p%2&toaD3zM>25J z*%0_4Y*BjX3JPkDQ`iZ`^Gmt@C!-EdrcqPtQ0=@s;q@F+b^P`@(oeCZZfvHr8i;Y| zjs_GFuE*bo9Aoz1XIWb1^dAsJa5{o@C_UkGvpruD%667Us%)VJZ9At?B$96ub8SF~ z3*~u{j^5mB<6QEFXuV*tA` z(ci}7;zoXj2&<6aeFbq=C;$4CAh`uQfiHm0xK%Bn9A6+%IU7wKVH!*fX8k<31dC=- zA&q=WBUXfJ5i$%!b?N?g#SLW9$sJG4?3HL6<&Orp&+Ll4(E`|KS!ztPIpKLS3Z%h{ z*`&Rwul0L6+fr_E);G0r3$~xIXXOJ+GI6EYC2tWxYek242$qV!4oVob0J zxnMpG7_{P-$Q6Th+5*u*t6ZOp2ha^^kL{CqU}9PxJyrH~H{v5y`|Xr1%l+ZJNRovl zBJ+sd_+s7O>!9Q5W<%QxY%=;{Wv}b~ogl!G92}7^G?tNw3H-zgJZvK6Q$gXkTMxl> zF*8FVR}*q(tD^8dQ*$28CB9=a5(ikE1u4@ZKc!JJ;fgTX(V6gH0kYL8iFcCaFegQT zJx6=GE@8~;1liSmRgU<240Y%PQc{VvxXt2xhbWis6vjjLgoO%UKclqMYOZAg*++0O z_Dom#VY5Jn3O%6R|nh56-3UK3Ewp19*!%qh+e`kaKd7X~p6y{i|;8P)kV*XPb zPCI7`XQ7^9HP$Ez^#&o`Eb1FC65XqefyS}h4HtQ2AHBp=ei8KNE_sG7<`p9r#@bLM zX0zS!G-l1*FK})pFemc{6isz|h${^O{b=oF+i>N|HJ@csxk3Pkf=yxELfa9<=%RV* z1MZaT!F(&fBCY#ykf5mGzAat;ESD=NSCrtALk#!JjSyR$%+p3nT5x_iH_eaHKWpL9 z98ID`9gZ;P(#xbc2_4*&NR_(N2#`U76Z8A?O%GR0n^?QHQCPvl2bhm7*wzP@4f=&i z+w%4k6J>ANqiS!8qiS#E1vD(A>Z5A!U|~Kg?F;ueJw|~zdNMsdiv!NVx#s%pD**0x1>-w@4ex~XHtx+&I?sa^CA>F#Dml$u@Yb{VLAdt zHb=w$7~*ER$(UKRmx`Zo&EB-_M)o6P*Op>o(LD#Gg$b2{|E1H#S-CGAWW%fW4lZL3XJ^9T*V_aQ%mq5cBHwb%p6U8yH158 zoL;m}Wq|f(mFYmgk#FG%#Yx|XGrI>DX1d}Nd;7PC_#61k=Y}``LdNOvOME)NzPcBL zpr1l*f}dY&vZuTNm0!G?zVt4Vp&$ix%b{!@RTVm>dLFx>{eqMRGS;y#Kix1xaE8dz z(QowV$XdN3z`6T8rTa&2>C(}E^yn~Hy&^V6ANYYCbGCpxeTioAsVJ({L?31Gnnjkd zf^!>;4&)k2 zdfhse%D9lvBlLXcHao8eNf}@>Y?H5BAY!3+d;^T5Bc1aQdGIU5_>_>&R~nq1l|xLW znln)}L;NQ}?O*O2 z27Q652rWb>U)2>2u=cbHg7 zGY5%1Bw`MEoAbah!!!9Y2~RH%B5$Pj3}AYb%#_+lG+44V2+qfAr#kC=`Eha8PWAD2 zVciAF7Jw)4M7E}ct!`V0rEa4La}l=H!M6Szf{{BymjDg}Z%BtR%=UIu42FcE%+} zk~y+byr^3>G=49MVutddS!rq*O?~uA%*x43i^_HeGjURLas+&bIea|B!rVn~53cOE zK9O!YUZ`Q+oQVFL1chwQf@zva9l2cvL(kf}x$oWqF zNulN>jN>X}s+t}P+3+FM+T>G8fH>eDLmg*vc?xDsKI#wct-XG}5L$F}r5a((Y0rWrdkpTyw2sCAF>LiN?~W%2=mo`lv8ex9F1E-8!*wQtR1(&vgDN;p(XA71fAN~>h!d52v|B>afNDSrz5py;XvTaA#KXfn<3Rz^*ym^(-LoOy8y z*Nb~UZ&i4<;;QDrYt1wYU&(30?|!sUzDh?2Yr)%)qjPPl{g!%`Zb^I}3}W!HT!fQ@ z?Ep?AXwF~8I5QtKZ>1Ij^+j31$Do$oVJR?!4=u^Ykj8Sgj=rT47nmxnlECQqsgB@_ z%k;F1*r)7w-V!+!2ICPX;JSv`aNHooLcry+Wjk56-)vJHG!tjt$1`b;&wREc3-IU8 zu3p~b^a{x;f#O=vhLqLW4;IEHawE9;h9r%zeHO{Z|A3dugZP0_Gb-PA5k5zD*zyc1 zX03-&Wsp>3g>iCZictlpwEH%tqSo55ikOd`w&)JK*I!_dp-UB!!7Z zflL*dkXcyFYCB$LQOCLEbS~oZHGbXKq7G{d;yo0f^`33hddK6FY5~_0N|FB{Vdl_1 z`{p!MY)t?NdA!ntor~pVx4eY~q&Q&c0ja*l!_;|2Bf& zywOAV^2)gX!wci4>`^ne{pK(C#2`*Gg)eYY0fGO2eA)k9SY~MO-+{&Q&%okkmF6(x zK%_z*qes#%nl}BVLM|tSw9w3anP0wCrQWJ;!GT~+6Nq~6|K{cDNWsf>Iw_>$SqYs%m;%Ym{a?iTC&S-l&$=3D;rH`=Y^lH|0L}m+Vp}pM-)7am; zmIArhF7Udc^m&8ttzk-wTE^Q4z}l(d@Ky-6m>L*sCr76?5IHVbv!q z9m{win ztA=S${fXXcIh%Ro=JR4LsnAe%erjt2V{UwxC`Xk!zkUcc3I>uQ?SNU@$?#az=%;SM z-_)5hZemJ;Wn<=1xioW+85$-4a;zPjW$0T>drC=mM?df{H%n&FWiz@Lon4(E2z4MwY5b*l{kJrpZu`PRy{>Hm+i zcZ{xt-L`crsn|9utk`B%Y|YrVZQHhOn-$wm#kP&go%OAK_Br?7(^lK-M_SVIfA-nm zF-GstZE(`ldU8;IQ_}23ZN5mUqJFQ+;PJ1iSY>}6UPL`XwN%&u%<=CASoC@U%hOx^_v zdejlg-9xeFjdsO!I7&&)Z|XLT=WBgXTa(v5CSO4Y{viq(twG?RwM>rJ}OHyfPFi|9c zHmWwb(eZYhCnTSl2~1cKA3`ySwt4!? zDtQKXQ1K08!VTw`egkrcY7{-AY_Jf5^jbBpdJ5K7Ka6JWH#Kv+fgGS%oiqAA60V)Zj@^Fr*BLN z7x0T;s$0qTyS!qpYafT~)D*W){FYpTT`XRU5P6E%u;|dtOy7=6ppk@Ort=6Fl-(m@ zBge`folR?b#T%rV#65;@K&!qVUoeP^)E9(&i1`xW40;E|u3Ly8q?|BT)|81360 zg*?tActsrzk*j_jxGFe%%HxlHCb-EH6S8i(5;v&6O>F+ksA7uXB{)w#dZ(2FtD=)joueWf=Xn0Y%On`@LCP`O zHF>F!*MZh#8~@n+cx06bI?VhN{I!7~j37^?K9T1#L5Qr)N2Ahgi^Fk~@V0<|dsrFk zmF^rX^Xp_041|e`Un(!EkP};|L|!RdZ2NLxP2e*O9r!bhvAc zfuf!LZ^ujj;rRcrL;r8l7BTzZ%%ZoyVUIr;@s?1MbH@T0P`E#4x2Q&Z>)Qcj{fEqZg2F?S4A#OU*ZGlT*ezs%q75W%>z0Rw zV3Z_zRtqea#Wb~ z^N2QPR%-)c>!d~a_!2ffHE+zId(}eLl2@q=fWND^D+L#q{EaQpbj5qBq|Kbu>;>oa zX*{I6%8DJC$t;gji^^aF&UVzoW+>C7GzQj=>pFy;L- zU64o}t-i>nHLysGTN7rDwpplWS2P}^ruXpWB@0s-bxMyCYD`!d6$4hlgp+JV7%*a9 z2Ih0|j65C`^&8UAM%zvjeW?9p+)nad3*xPtmd8Nr?7V$FTom@{kRtB%kA6je!^#k^ zDbGYP<2@(*Q9!U=oEhNN&B#(+$5!J$XRIeKVA5nyPFBPsBfPV6{w|M>|@ z>dU|^F3%p0(u@q!Cm_xmPAMuOw)&m+`~iKj(KQfJv~1YkeO69wkQib56)$aOrBe;d zy^;=_KryzD8>aoH(x2d5#r6v%I-Y>kPzTIrzp%L>eYJQ^G1)nn&9!01v?VlyWCD}5Ey}%RPGd`M z#VN<_fa3-^-X>nV>d%h1|mxg$?yVsG9C3#hNo5MW`2PITB*( z_rHBwGB!76PJXIpzQRo>@s-)~%Xm~Y>RBy^-%AfK&%oPFs*8jy1FPd!o3 zZOd}p6`NU+)Y)K>WMOv^hNC07k1#pJauY4D#_r~l$QIXszlqiupUmqr5~x@^^Bl@50w@-UfGcv)OT<#lp))(uNJqzK>69hJkV?0bSLlk zbl)gsaRV+eb+IGggfJ}eR?kLWg)!E#&T|>~{O0*|^M-U2IQAeh*05;EBXb55HB_*7 z11+5HG9UPUDa(AH{OwxEVsj*8>sGaDJ#ERpC>{N{gfYmO=>lS`xwZ!_-xgCF&;p~j zKH3pn-Zka)M>H#dKyICSrd1=?g6Jfw;%tAT>Ao$i(EK^8oCQ+jse0fygd)Pfv-QIN=>p>a_=W%&**lvV{5RO{ z|L^WgD5Ya}B`N*I%hu8rOeN4GmaHDr=?Xi>FQv_HJ{}T$eqDJlU}whljYRGO3tZ2q z?SKWY8`f(qdPv3d9?sLePdrBloG$;hBF5Ts&V2RQ4*`2LXomG_B~IaN>&*Ja-CDlA z!EuNxXDi+70kbKai`!sFBlhwW6bA#f^hZjKX7;ljol+YOB|~8+T?($<0?NLVnqS#3 z{m_^sn1kEC0dNSm-dMZ_DHYS=)OmBKDWoPK$0<66rX(G+bEqP^Idj>PgkK|_;i4Uj zoq8&rww15BFiigT$h6p+R`z(`CVuKHeFls8PB>U7DWWd@u-(3ag!zZkKC#(Mdn`hOngd%v|X*xveY&IjOBdUy%cR(QOIn z@uPtrKQ&Bh3KQ}WOCQ$zj}~lHBk;FO2hih(w;Hb>G6OxnEt=C`k6&f#)<5Q9J>Ivo zc6+R>Do>(V-nd<%@2Kk`HvW^#P)RjWoHZYoDM`F7+ah=~ITTJ}P*iv26Ux;*sK0E= zi`KFjv*s?GMLao3xs9=CRoez_WnuDW5vC>Rrx_|*(__3!nV(dI=bEjs9lMMXVGF%B zK-tsU8TD9d1%nvvrt0@bWrQ*L0SNX6c#X;Igs)*f*duyfW?pWdU1Jr%Gk~TPF_UT$;v}7TB4-vWFw8=xN5wVDVU+h zMg`{P`ImBD^er4rgM;x!IbOtL*|P=MA(U}MlEC1h(-oWL_9YezS~H$^ z=u1o$1tm4J4j-6(Ph~xcY~_6i%VRjVqBF0vUT_p2v_Lf;w8x{83QfeYV4E%)RnI%Gp~utJtCtVre%G+VCA-%bu!s-$eK+9lCcWF$+8xd{uGy-5q7z5(x_ z9mat|*V(21yAudD(8IEhu(-ayru?|S4s})^6qhTR=09kX$TAa+N!H+7?c^j2=ijp2 zW=_Si6UJg~msfOO#4T}W{yqHT^<=>^!~f3u@dDzv;?I<@TW(wpT&$a@D^z*A@f9}- z&Mj%GTjChgVk4aUkM>epfKq%5p5V2}et+6y!t$T!liSWj8aK#$Bl6$4Kmq~MpICr-X<>QiEY zDe)#Cdp9gT+1QU|?!)DtP8I_~c3t#nFpodZ^UHy`>^nibH{t8ouq$Lj_s{|SGB4lD zil^L1(&d|p87}8agYUdmdunFq=y}KzL~upEfO|lUn{3Gqv6_=U8vTyZ*g5)~Gtn)g zGd0%95O$6EeOdmed?vhvEk~&;%^NJ&CCefW#?rY`CM?4KU((abVugGmFR*8)67{Hy zn6Xo&&dOIfZCH_qwM+a={;znj@O_-qPL+VbEl=JKD#9TT~eNsjIj zh%3f>v<##_MhPB$5b?i$^|lPos~KqSU0v2J*R^%V2wk23sYvou5k|NvdLfJ4lWai6 zmhzX1>00`Y36FNlzjnwVVXP=}KnZX7wwL=VrW?nF7O$RS_yR{wFY>r zs=k`>pvwW;em#zjXv4b>r%R}iZ=86VmyT6)#wCMJ8}oJMA1<9uSiqHi!%(ZuTZVkM z@*yu`-u?X?G4RW_wka~@R75ysKr5Y5GT9374~$P|Z*@N@3J3TlMp|n5l}b`>6+*EY z-<(ISdEliG22$Xs)*#m<<%*mgp#v3+y_+xn-w6y>^f@CF+-O&5^{xd^A-f<_{Jd*O zv}r<7L&-gJiPHWLe7+YQ?SX~REYq2rF_I*1OQD1QDi{Jl*}G(4>CBr*k!Y9MVvco5 z$35zzIcs>RUVef=#>f?|yob+SxgWBH=TNBI@1b$@F-~jbI83vDCNU<~+7&1{C{LDx z*T(w^Ct2Suk6grC&WdzL)d*##FDtPUoNn(SxDh{(X%y}=^isX^0?qgXReoJl@jA^C zBsUyw&OEnqOMkn7Zs!i(YwQ-0rCh_l$kTrOY6Vv_U+zv^$ap%&gmt%A6UQRpp>qh` z&SVgSJQm3>!ja$}IU?)l75qzVxFw~;4koCr1ls@HURDj}yO07gm3ZNk&8Up};qXun zF&F<0B+7Jd<^520?psUP7V;-TUD#15fBc-uv6>rb6MJ1)3OQSy4eHxTV^~U2y+(jB z#ml!u9+_=v_C%hKa&B>Td8r*V832We*jiL|&uq!ICE`?@aKRP+N6vS$8c-7NUgESI zWcLMydNH|rSl4m(5tLvlj??4{wL%bC93hNB;TADCo4qUq-JPu7+tvkWnojQ2GUn;fRd()P*_f6pzM>lxBXfKr_P zZ>9J@jE(=DxBP!-s2+dC_)`mI<*yjO`i2K8u#%FNDZJ$?e-(xe4k;8qD`+KMu4Oqd z?qE+`67~8x!MtWJWp)xV9iB4%7{}%h%o(ubadBzzGn`ik%CQUZ(DHq1&*kzkrpMyr+Eghu2RW7 zlSuPmT_0;ILz$D>H7JGFiljp6;rzhUdNG@^oV8Aj3Keb)qQ$C8N~G(>$qF#8%uzWUSv^$?yW}WNWKK5 z@JU(VVf4uFHP=$+l0Df?MfKC!7VPt)dM~>HX$j!L^I^l}^EjVCWfnXN*N7Sk&oO_J zAgTIFX6+E(p1*Y{xn8Y6CcdZM_fVXn2I-4uZ)Kow1D7vABzFS7g@Rg8b>rsRj7vdA z?o4LPO@NI60V;10UTqLu5c8vmPBl}42PwCqwtXr>flL`m{pK}O*=a5RSD}cE4Z54s zaU$(cc@@kooG4PzIn#qkt!4f{&($2XD~@iIC(J4~0)%?t40PsKbDXKBN^uAtBeU^a z0>s324a1wXu-`Kehu=44(u*|Ufagba3ql2(3Quy^$o<_~Xm%Ts{G7Z{z`IL!$l&+S zgXK*G7t5^J@d%*{e{D7v`G!*W6i}cDt1?R-bD!hcSg_!>uKpGT$LIu30gXL;-`wS;y?L7gZn3GQY(QpK0{r-p}#afqic}e zUF9T`-FXeA_*;ccUnZwqI1r=LA8E$9b*`&uM-<|?0zah($*rkY6zpI*(H-pDqGFW( zH5ccG6O7600UGC^aQYkc{@fS~_RH3|gh3SkJhKWa8Q)iz(;lrP#VhA71Rndey+{P) zyH#WGlxW2xYJr*=_`ErbGQGtjrHa7GScLRxjJRc*-IG5<@*66Jf5eH1FWQjt@-n;0 zk=^3R)BmnC?GKya$M~G*=fG`*II>Oo=`L)1+f!Z>i$UisY&I@bkEG2wV^ghaj+Gjs znQ%NY!8W6j%}xdI%naBhNw|LWi1{+S-oQcpgfDJESnIbB&O*g8Fewo)<`prS-f>gc z$!*d5mm>38!9lAlsRYWC;;X?JWWN%NbDLdBZVHMrHATtUTjK8QU$b%f++QSsiEw3i zc}f5%Cfvs481Sb6$$ijqiN=1Ub8qa1-)SAnfCSNWSzr+QD}{;&$la>?tDhH97u#j`2#*)K{Ja<2W@YCKxyTYzh-4ut$1Qjc*)qFEbEkb^GCM3m z_QAmG%4b+Fn5oC@OaRL(*xV=Blm|N24a^d-);zC6n(XXe@)3s46#~027TSgLz#Q!) zmimF@tgk{Q4okaWMZLH0qL-Wz9Sp;pWrcD0MOf-h7oSh{2+^undl;ARA4#6ceuVVH zclkx9H)P=yO;e=YLt}8t=WZ0-NEo}__{7?A_O$T;fHgU>bn)2~+H~mL24_um6uU=) zB>Q`0ISN=qyb5EzqJgU9Hx8F5;PnIs^8k^55bj5 zoLSqgkW0B@96EVe0vlBI@&;#_o#(am&*D;xnJSY=-{F#BeMCcs^LuOWcyx2dyd`f- zdJg+Vcq((mR5z`wnX@SB9-Mrg&6#o93>)^Y5uGvoq|=ck++-OGvXF5V%8v6#<)K=mo+QGVb^ee@GOu)k^JCPA_98@U4D znwK%#x|YpAEqG$_!I_y9$6tX>m@sf>m!BcF8{zxSEA9Y|4gan%_MA3QeQ)xOJ^ZOD z6v7T4!`CsmvLZ`MwGnxs=gRKWB&%CRJD5z8egodhZ0eMWwczi`V`8n;H%_HU50Z*` zY5F6T)=XOEXnXC26%XR;x}s9BxT2NjL$E)dl1TkiYbjDn2j^5Su$3E#1 zpwE^XT>yL{V(CE9WtF!RYt?{nrm?RxNFtYM_`Tm6enwH&8py7sIfM?xpDw48RD?j& zha~GBYcLln#rH|9rSIJqqB{Bw{0u;+@W_6a2UhNLs98Y*A{c;224WS?rV%riN? z!mG|XkB(n#akOtQPKW|Zp88L+wlH#^G0tVJvI%gWl5R=fa9S+fQEg^zj0iu7+4n#{ z9s=m}Ud{6vi9JF$N$R6aEeqO62bnvib?go8b=cqhAc8^M%m1KN@20c6;IsQ*oB3N7 z*O54c7Ef%$vY60y{n&SFWj&#X)Hu)xUrBpPCihN5UQG~ze!@3GkE;z3q@S&4dBALp?|K8ab7>B@DCXc-V=?5!j z4Ai6dGc$}UD5U_tMM)BmxmkXP7xT{+=r+Q)@Fa~m-TUBPL5LDSp_z}qhaQ@+;xq-~ zZ1j5c2GiKF%wj#~!xQ;xt1NI5*pl6M{;I zl1zk>aKy{L9OtSbH$OviZE{FI<;NtLo)*coM3{N24Ay~uLH9(urgjWe{}aPwG5HDq z*Sg-M3c@x5lR~%uw(;mco+3E@zqC<#>o4{6PcX0#?x(k4okTz*8<>D8WfQF!JQ}Oj zGZ6@y4_b;LoaA5Vsp9pl$tOsT*8|9O-HgHX>?^RQSDGNk#hD!!E<=Hr&(PTUaPw{B zZNtXoZdix^6I2()`1s9+pn6~?MichcRtEu5f5bo@c)2;wC{&xj{S~}|aUeD#|JzX$ z)>0jEf5@{KpS46x47z9j_B;h&gnVoWOf#D26jIi-2~Zc;d4XAD(+qZajnd#1xd*Q) zq-tAhMz{m%icDn9BO}R;@ltf!hU97M-<$>Y^vWZK=|uS$LB=?6B=C{2>lC#1zm>;% z&Qjw6l@o)V)oaex14t6laxgn?4MHPYZVjcUxaCDINai=Q`RaCDc|VFaY4On9gq0~@#lDm*=C3Hd zjUTo2={?s~j9`F{x#PvK1;Wg!M`+#QS%BfJZ?g3yIT)YB>g6sC9eBwXXs$hi6EZgD zCd-eaY`56+~eXL`2>eno%mqv~z)`{Ol>6xbm9s4aBQP4g#;TEY;u27g>k0 zRomAerkP}kKRn!t7Vg9tKEqO!i!1&Z@P^OB|)Z`+u}7M`y!W#U&Az*By9)!)e#=;%z$&D~qf-4h^m35X^e zD{f5)-Gfa;KOe}`Q|T?>aGV%s{2sz9H;c`$G?*l@h15J~6 zx{E6Nn9h$^04(%HKBB*wh1fb0QH6Y@KZBF6kfixV$K%$$f>#cq)jM{FO(hBUk_;N> zPqFalRw#jbX#e#U+K@4v@w*?*9M<;=sN%R@Ff#WjXN{Tr@9t=tHbp&r@nrE(AJ)2K zKnk=$DJPHuE$W2F`i8eLpphLgV4BaldkrWkp5FLh-I!ME&|hHLAu@)fkW`-sbpvFs zTNuyke16|uBql}iIROhY&-xu-^|-<&I~<+U%U$r4Mtl)N0jq!f66dFnj^jaZmNr1R zu&=)&K3m~juQ}rQ)3r0ZUzC*$&s&HlgE>#r&rXWv(L~RBD2CKLKf_?~0Gl$idwr(k zmfw8-$#h>?uVoYfrI6&`QlS5bnQl2#8%G-lAtOs8N27oJ{a-#&SxfeBiNgFGT~$Ob zg+E33v;2>$T&T71h5X<`2+%QVwY9uTwT>ok;rl_+&q&1hXLQ1MknZQ0NE3clNi%31 zLM~>HtBE$?b(_f?VNbUgq%CSp!IgKn^!K;{MNsH<8GRh)OWR@mOK0MQLe7ev>|iy& zDNmx7`ub)D`fnIu?YGW@@*=`jILcZuLKE@7PW-i~OOhH5+l)KKsHs01YpEi}s*S)< zsEyn7OWmV7OnzHWTs~kaB~-7sH^`MOEzT8Jolns8#v7I0Chu^-!r@9s#mkFs(jmXP z_R`@jmCG|nIZ5(4LB1{&pFl}nLDr0>IGs?0$R~dkVGg7nk&}I}?hQ5=4=N_^SeAQ5 z7gX%mAHvMGqzs&$(#2`M?S+i3!_R8ON0ZhYb;pg*mDM8&X-lAI<7$YkHO_B_p(`Z} zMMy>1R%XWkrea>}Q>mpssMWU{?nmNSMN$7j{VE3<06C!7L|~0+qy&s`?{xo&Ty%hS zAzus^#PYfP@5H95lvTD_H{$@u##|RmI2N!RgZC0_E76!WYru?rPA) zSH{SpK#dYMQ@5PJ-$-%HY->y`=u=@~@Zo47(l+h__mY9gwq>ySyMonP(Rr^HW5(=k z3@5(mY!y+-9!Rj`K4Z-u5kXy{($$zIJ?nZ-T-wR*(EZSUaC8F}O6qHg2F1))H1m}Z z#k~HC#b*DHw2<_e7b=Avld;Co-~H1H2Fd3MDy)Ff4dXkQ^=A3sF0q$iul(P0%pv3% zCzd%!Q+WJA@ca%%>_MbG#!{)J=0NOjq;3GX!Q+`nh4@*DFkrVmiB(J`gD9uK zDTg$Qm&FVlDS4Oc*}E7cAxlv~%=?O%|De}p#l|CMLiwRiTvK^PxoP!3e4&u!&DRFk z%-aPS6sf|YuB}k24^lQ;Us(oIZ&3J&dcUt)9_HJZb@8q4D~0NBS60QK+o+5f`{+`m4rKti9AgOUCJ`Qa^6wo*YAMe~M;w+xkR zq9_eb4_!d|RyOETY9(aR0E ze16KxUQN(tq=FS)Kpt+kIUMo1TzXvw^Z)ttg860dx!K`t`ehXCmGO5_e`>-wZQI1S z5s{xTkO4#a4+91OpJVpS1A^aR=Sg&6589$fUhAK~p1coaz;NS?DO3;Xkx?xDWxyz6 z?E-?{*VpUqlKimYHUQBV(DN9u#zjNe`m8Y>B4I>Iw(IsX*D&-Qhw4+6>TMc9@8KpZ zi|MJ~OQ7wYFV}BcbwjJu&TbVN^{dnxWfR*{%Tu$zM`fMpfrd)~7nWN9+i+u}UPoGo zqCT6^ps9iK14d2LARq(==Pv}Nq1)YI5jol=s{-#mYQk`?p4KjHNAWeu4L6vv^H(md z4Zo7crnjgir>hw7XY>!sclz;4)tDEWr*cJF30Fp~Rtr7`hvWo+-BIB9WK$Sso^ViX zS*L$ZMA*(!V>y)@f5)pJ4|+JTyaAL<9>V}rU&90n27Ar8KuVx8d#=O>pOo80Y|q7-0wOY(5u16UvpehEXhp+;2Nq# z=gKwgxUwHTP@hhz)L^{}3R&ek`lGe%Bx0}-mOEGhs1N#MtzH)~SdVNhn~2PRt}^IQ zF%dY`ozr%{EXoO&raE&1+Zf8OeMax~Rk)Y7635M`_A-^4rR5#UI$zjSirZA3BoaFG z4-cU6`)i%>`t9(=;f}>d55OQ;IJnAFi}j$l7*TIScV;?iUy{&d>do5;WZQf3Mnj*% z)H{BeaI9#yQL|Pk?9wex*G5F2&7Jj+h~JcsHH=!EDb?R9#3R-JhEfw4d4*bq2%I%s zMNWz5(sacMjK(1_buBf(2gfHIJ3uBldq&Y=#Pb1faQb_S8V$j4FjTX+J?1twosCa_ zI^w79blKx|Us}!W)OZm$jrhPyu1sLzd@U|&l!d7r^I^r2FfuC(fUy#`F-?xXUhn0Me9@xq5I2_YBE>eyPNx| z{`xx;qUwAsHt=IvT<$lsda1Dsxi?AzHd7gdc|;y0GB>cV-?V3{0}O>^P*0FK<^J+t z1V+2|nL`sA5oYOx=`>zohy)O=GJStRyyuejcZ93icv4ce&dd+|2>fRUHVx)v}Q-dw??lR{IeLRJm$|o#MD*| zk1|r=DIB_M}X}eFA)bvHSQUsvh(uYZRR&DE-0O@@$}y zorXh&(hF-sJh|j_c=bH;JUYvMf4QRX0mTa0yk6cCu17ydUJF{me1thMC=~GXCO0YIAxEmV}OQb~Bt8UB(@r`az zr?vMoI96TZy~O#H{unS=Vi^dAUgs}4f^Og~I9k{(Ha9<&iK`j++YIBK)Nl3S z>>TZ(bC6UGY&fK`!m{Wesqn8!f2~&DH&KHqZj2`{#>OyM##GXw?9WL2Y~C-k`@NG( zGEFP9irHlIg3|(*pGf73G=5$YvyfO^at@`YXaA53BZLMW;QsA7FBPww*bhEjTPwv# z)Zn-rx-WM>GZ&6MCqOJIHPh6)EqHotif>vxMNGN0Ekv?huSLX9Vp5Z1!cNdsk#hD& zR+4lVdPli0Y@b;p-cv6Z7jc`l=e`&q4z&IydJ$y5T&8Y{4PFQFsEK{I$w7IVknvtw zy-fiFJ-x`r&pBc;9<Y6DAe-jG{iMz{x~RZQ||ZNjuCWWZed z_$i1P{cThkwDzbckMl!X`|0Bkey?nA%=&Fy~%$aqm=4GU! zx}5A+Gh=%l?zcGdInXG*;Xsh}daD2|`wbHc!>^OHH-P6|l9*W}LNB;a+0CWWyR{7< zbODL0L51<}%QF7&%d(*Io#}k!7?p+Y`=1~Cl%N_HKxT9gnYZZt6BK!N-ifMu4soMX z_~GLiL-q=*^hC zuMw{oLdo0GXx&TZ*=Ab6`oxAGR>EU-$LosfxP%DXDd5z-1LPI|u5(WVzmArM?+3eF z{BoxQ&lXmuuKVXG;9<4*%3#gNr*GpoIW?}E{W#UbXr@17Q|$2NBY27y3R7fm5y6b#$XaxNsDeejVl61hj{7QObKw&07^j07OCK& z%Z46439f7S^GE41De!QkepbU$h)>Z`puMS56$ZSZd{c#vI{Bw~iC|FRUQW3Q2TNP& z$T;n}bJB=7S#!Ov%=7bRwONI=kM7?7?emu1X|QBnwx89& z0RUOPV$|3pe+YQ$jIn^7{()A|H+&~6{Vw}PKrE}j@lS0$qZeg$*)KK=WSYmT2AAsS3Bq4w8CC$_jZ>?J#@N?fM1MGmz zp@D~({3^?sg@HCVu~c5DV=p%E+17*UeLkg!!hd|YFu^#eT;aR3^#Z>=n{bM zcF)$F3VT~T67Sv0UqyQo%iJ`o=BO!V8`n9|li>wtp*RnoAev~~PRR9a%q$n{h#zDb zPHZ+RhB4LJnrV<>e5hEp04Jzk;@ZV)$2}J1&2!hbJhN@xT>RKhiSBAKubMS?X6#}Q z{Um>E{HOb)IX-&UBtK6Du=*rdAQh-4-AfVc{fq2k7iq4_EZt<2BtO2ak}?Qb#T(n5 zf}eg3=#yQT1usp?B#R^(T%H?gJzNeW7W%l{H@GJiRY4Yut|CsAsv=IrgQ+HdZwRrU zsB@HGIfg1RC@9!hMW%E=&1+)wT#tLtv`ZAz5g5YVV#}~8UByyG8*kci}f-Z znQcsl?i)h1i_w9YP(91uJVDP;>ORSG8?@3MJt z26+~61X@yC49hw(H0fpAwZr!Zah&@?uB7qUJ1M~c??c!I@Ch9=!pS{f;mKkS2F!_%tC z)yKu%g%>%HuOrL`%7;fJ z8G&uX802Ehe+J75zbFoUmZHfKz2=bM?2f%Dill{(v!qLywkdN!AypFaSYpAe#`YAX zR!I%?9mI$3%^w;(EL#zhj5HZf-zybnB8x{upGX(xx|&D>ujnoW&W8d|)WP0r1;d8q3lxXf9!DamTJfW_YV-f()Egse_b)S5x>U;}#$cok5} z%gNa(PLefLOvkBdS`U<+n?~?Nwv9e;o5%1Xss%z~P}_Jf(vp`Z8_iYGvya&3A4{f} z`o|T)H?kVO@@4VpSPWJ~W-|SrTHVEC&k4LNWVk_X*wAgKE>>7sY zE8?AQ>PrBhv~NqgL3c9vllzONdS0sx!+f${m-zfyf??+t-Tkl$Kpw)G!n9guPHb9) zvrl!wieWOj>i}KH6(EVO-IFo0KL0M(mjsx+o?Cx?iu&dY#Xc&}E`idHB2F);#w=kr zEg{zS`Fp#8v;{9R!bm{CLYp2Nr)Fhf?t`2NHu5TcFl}6Ryvq!nJ&*EORI1lk6#_Y- zt}54;2Hy69K(eV>;#h-SsAF$EQX~DMw)JAVSxm%+!A{K+x-05_Qu*FKZWTotm}y&M zlb4euJesCu!^q*U9T^L^^T)2}i$tDCMPy$3!i++n_HL~S(jOfe@2_>t^oYjOTj?LE zI<}&s`zw(_Ra$}T<2a=8W;ATH>^Drjn0V_QVGsVmhOm}}`~IZwn^M7O$L8@iJVssf z)B{r&YEbi_yQ0hPyJI+y-ei61&(MNNys5PJMiJ zs9CuAnUlTF(hF4TxEiZ0=EdB=Wt7vw7!3?e$BozYp1Q(t+s=^}WW9p9;j-$BEwGh~hxxcu+S7Vl6ttT0EbOB=AXBlem6L zmVZL|E&M|EFY6@<)Lz&HWXBNFH@0ld1wJB}(N-saqy3?r&R8FMzNL^EIsq5cDdcK* z@6y=g-?@s-`SXmvpoi#{m$;yZ*qx%XW~SK?UnFHtcX=^&nS+bAfH~|LJSm(0!F#K zGw#(32&kYY$N8Mn_p;%JB`VmxXvz4%rwo5^df+4g0izRcWusU(FQT_NBOr+GD_a2K zyRymqN@}j%Ga1()ycae~J)>4-GMxx*r>IHE+Z_?#Eh;}Twk;|kxkCR3FZTUG8P{I+ zUfRH><-q$Y2mDVRAY`!kto9}2WMoFs_pjRpd$4~?1-jk5f4frV_)jGe;0PQ5{MVyjapUi?&7UQ<5sG{{NSGjUWO1L! zUN)q;*wi_s5V;lz8bTw1zR?CcQ!qET_Aj|V7yo1WlvmB>R2~} zhUlU+T-?!9ZNQp)5w{;zP|G54b6Bju_LQEna^sAK0o-9UpNC1RzN7`Zqcl0rvMqx$kz4NzH zJpG6THPG^8ySxeuwm{X-y3MoHhTE_#z=IQF9fCtCK2&9XD*FjEZ z?WtuLhSvo0#@n7gvBgqb%(fmVy2XPdN@8}0zR5cmUdUNsH#S9ogxr~QNPf6?tv>}x zY7SZ>m`H`~E{F#Vi;Ci{7JLtM-xgvQ6$!`YFd{}n@6(!YItf!ddILqhH$FIiAZfEB z5M)7|rxSKS^di&?9Fnh>nwc<_Ia)~=z-Q1$9bzC@Eu}|&q7L(r-9akTkp;hZ1U<$H zlLY6Y_2uZDLOGphqA!URgq^M(mk(B>=u>AKkTL+z{ zAOqZ=6#v^kYqtOCXZG*g6zE$1&y={;-xCn;U{Xsc+=#?xISKfDHZU>Mx70J zFG=i!?3(dJG+NH{8{(25@P9x)eRF`QW~ht0Bh!z4PVXDhXLcR};O+z$3gqNQ@NjBs z{f);3xCCYQdB3}(|1xwcA~#Xv4&I@T>LL|^`E}VYSlPsC za?hX0I4y&GRVXMzeEhmoAdM)VfVBL$5I`O*gKbQUU2RsW?)eh|*bplnvooeAk-}eT zC+j-EdnAilk!+B7WuFwVpUq$eST`Igmd&)wV`v?iX;$E`YOZ$)TuR!8D;ZzkODs0> z?muc(AuFO-Y;&~hcQxFX`d(st6f_<+c@wbEM{FFgd`-)^DKwJK_4VaEs~124XOiv@ za3>>-%zMHjk6$||+!aXJWhpf{zem81K9js(VZ(k*7R&wAD7`AI zJrQNc@{Tqb@J4mnAKB&EWE9>JrLc2t7`xcPLR?M-jC1Ts>YpxJDc zEd>`eQS9@@M_4e=WZZUp)Nc=4qa{_BXG0>mv;}LT(cD>6x!7!DE=5v5ELXVUHX@qO z%MxuBl=>fB$Kb1@z&w80-nc3UJ47jLl&2}Db-=m6I*;0Eaalv9qsW%?WTdZ^&r#8r zK=E`yW|Li4B4$7Ih5*n-*Tqh1Ys-3Md4CpR$90S3?k+b zQmOt(n)$|HFH2Zds5rqH1nS)zZJ8JD%O_d1A3XWF&(g7$OCfkW#HB~{H690_-K$oM zW{DGC%7x9FD^{$N55*ZHge3bb$g?{yv^;X`dTf0)&-%(PKs#2kTq{JuG*FBGyYOl- zvQE65l(3_C`(a{uPE;qnpvFft56u#kBZm&}qC`9`QN!i#HzA!QB66@O&+X`uC{3AB*yY_Rv-gGfA;HLyB*Io!x<7sYE)EU~ft zMqxl+UO$(NANfqA-EG4*^N9$h4$Bt;>WOo4J!1Xkb5gi5bl4v6wdk}Ba%&E6Cni0g z%SOyVE2+s~d8d;hFn+heBc$}vGe<$)3Phjw%erfp)i}uydX&T-J}t`}`9@hU{fuuL zD{U*#R9W89isFN(Si?o?^xW1TJB^V=2<{It$fzbBsVZ(G8dS@oZ$_-C&#Vt{FI)?L zf6vHg8~I07#{KD`;Pn^dz4U17iqh!<-*3i@?ocvEg0BcQenRnPogY&{+vE0f4>_V4 z9qaM=+5Z@94kup)8BjCadfzE;$kVikcvE#FS2Hmy3y4QmF>?I(hCI!?kh?dISZT*` ziHQh_R2AfG#C;{gcY>nv<9V)z(fQZa=E+z0j+*hVPg7dzfA`);-oNmU?3(&u5!bzg z(IoE>?>MD`r8VGGLx)}WrC&DgPCEIt*DN`F0o#rbkqnV=^o|;|g$cU6Z`g>l-?VUu z?4e*;GzNOq?|v=p8)>$?^nE51Dimbx9L8$4p3D!PSZ0&&%U<%@RyXu?8dmjnni@%} zhT^T;kkw;Ji#ZEk*J6CW^in>?5nrH}br`SsvbknXv24hVv2(22*CqW&5sxTTmeRy0 zFG|ng)Y!Q8q!h`+TWG)jo8I!o(npi%=ifU%vsgt*Ws&V0*yuSi#baXFT}f&$Vk;t> zdN%)DPLnXOtSCGuC5H&5N2Z_PT{YrCH2FfD0P9kNDWax{NkZL{Btz`~}* zq$l8Q)F`G^M#wO@EnulfYK7y>c(cB{sKK9TGQmN)quE`#_dCY=95?bKU2xYbkyjwt zpXskm1j00tmmfQkS5O7r`O+pF>38D?j@yrF=P;i}MaL7i_~M~69DXE_Sh=m0a$ovp z-c)1G;cAK7#=BrOUK;0se6cK>BG-~@jdiGG4B1*t$+8h$k>~3Z51LfkKNVgmbCMXl zY1=>CIVWCU{VG4@#o>;?>!m|}2p%xMYZr9}+2(cAj|7Zk z7qfEiOpw>(R2V%a7qF_k&*tj-pi4AQ@CjuhC&m{^-a<~WJz*=%kdVPXuJfAY8=l&O zP=hN{6AtfQP>-f~TiCx*mTgaQDVA78t@YM^)MX#j<>cPq9z$<>xE+$xuGi#BfU65G zvLlSuPPbMm_in$TKwQkO6KQ>Z>1;JI4$r8(i>H8Y2Bvowm2Lz7xP(dd#&xQ@WEEE_ z!v(G79(K(WS9d5HjD@VlU>-7UdEZ(nL|99BpL|ZZS4oLs#o!>XEh=WcJXjH_5`8r# z+_}E$GMmve2F5*ZrCc;K-e~j{A;HV(*frD5m)9HIj0pV364Bc^dDUKLyncwjUZk9I zz2jQ1Wp4hDKBfKG{-Ta7?zOu*PuWoquG|ZJ%0Bmu_>jyk)BFepk|Wn`?214>BxnS; zt9{D-f&Y2+KU_Nab~Y&Nc|)#K3YWyF^)puAq(hRCTM1{aOI8^iL|{EbsOlhVJDfLQ z+jLhvtF7I83kRIJ&gJ66a9)dG|Mwa^;L*{!1iat3rQTh4O27A#pZDO~F8FpfX^-}c z>nLqPB%Kiq>DY3xE<8fF`MS3@_2pj^1am&k7+D>ZjgQs`8I{7wQ&=PNvo%x z(w4C2Nw>W=xD0iYAb_}6FVOz;z@S;FJX-Y9(6_oKvFhn6D}4kY5v*S6(nu{jmCN07gw?TJozGjRQf|w#7XyO&#>DZ%rgt zo6Ic_?j!LFCm6iMVi~$^mSH&{mtbbuljBj{XDvnR{0T3`#5GAcE<~Zn>CWo?_TY(2@x z8%gE|>AEe`g>kkKmt~%4*b{RiFAWyF*WDgqWOnzpVlvCvDa~+1FCf8E#o)BQcw5yFc$@88= z$QQyA6ucw*Cf;9R!r<9Z>CN|NohO{c@1kj`0QVmizNhgjeNW(3{GJPP;>hdr2Zf%M z{oH$*pT28Lzq+Ntev4E!22u9j%5|ZOv>G{qBSMbDAFtvOjdImt+rEgg#Jn+FL`3+G zxa4uutTd+b`@j*$Br-yWhV(AH2aoWWs=uzj_Kw&%oRCj&C>*?yvs_3{kQKsQY(!4p z`hsg!VAN_rTz-zlBFuXwRGHg$vT`nwyJ2I1=d1ep>&2+Mk;{3!v!p6iDHI3JKgtP} z6IohP`yx}9-3mru_>%Cvd*Z+^9yu!BUN3^6nkssUvAOn6Hb14nEB>rSC-c?o$rHzYHq%6(%aB7A8T1N1u z#RtHrOKG;wb#}E=RkM~V4>r}J=H=LsD4+X0+s*dQsxG`BsD?%?LGE2aTrV0MpT^KO z&Vb|=h7r~y`y?3ysymp;hhha0kMtZyRU@xR`Hc1TCuGj3lAzIhi+$PUPDZ}>qI}Nm zvsheA4{GrF)?#WG;GXZ9Q%%}SWHSsXE4M$oh{vLml6kvQd&T5y0C!_$=*?#Xn;rY- zH?Vp?B4OI{V!09!Nc*3=DCg5^$hJp&^NU&4jNLE8)xj!;`w0Bs(9n z<2%Pz)(pL?6SsWrJRGG70*C0B;umdIbFXFybaO2VTtsxeL$f;C+kv}1d&w$!xbBLF z;&6I?ar#$MgB{ZT(2gtviN|+l2fV$=xNdrp)kx3IxZd7>c^%@6Q^|QtAX|)V;HKF8 zrz}{2eV&jmi%{5+0e+jW{F;jhjea@^`NK;)`sa z3pP~(pWljXejo>_S@`(Q91NzbXFW~N*?F+E&FdjJJ#N5*izu`j{QNm6SWMo{`2Ku; zW#;EcZAO=et1>R;4hDat_1wIPA<1H?qhNSBi1Z=%hvwgu+jPFUMD9 z3u`AlO}-zEzsDzu_K}>4oQD)Ye8D)cT3yfBkna9=woPN4zGHZbVh1kgZLG6q6GbGs z9FkKX?jlcfU{EZ~mg`rOD>klRSlp-5ByoH_U#?FbZ0Q+#W%pQ|8;Ehj;;nx5+{Q8-|DCGK zjbik791T`p>~Df5GSx08)LDPSZXdwBZ?KWdG=%46w@6!l75!mDtnsavST12j#zk0~ zuH~p5gJXC}q9yw>SYt2I#jURS%QU^~Ft^!_Dr9=#EV;GH%iQt-uf*tqURBF2qH|U% z9$1{K;kWw@>rLL+7(_I-xvZ7X96I-n`5QDpwa@Dr)l;vRQ#-%wCT!f`9^rb)e0D}% z=?wl5y?9qK*R~q>MgRUX&yJZqnT4Z?XAdT&zCIn{co|bl>JmPXFP^I4Vy}z0Rd0|Y zq)p<)C4f?yH-}Mvdr#tNK_b24ee{YT8xwbT3aYK7g&!!N!u0OPe39<+5PSJJMw-h-U&+E5WVGB~ zS##iOI3ww&V~InXQRIBCWaZbzEooQsn})x6epJKJ`qoXFF?nIk;cP{m#9>#LNK4vi za+I`pPU}+G{ElO@U&Y02J9Tn;1G+TfJFFFLYgXt?seE=<110gC(y)|lkXxx5T^>h8 z8Pi7H)Wm;=-hVe`)wa$CFU;}7LmRT~)$%PNm+*Ectjl^y%BHWhe(3I|IFHHuTo(!4 zo`@cxlxk)797xEvO3U5hZjqUj-{uwpAMtzF<((h8m#4b>*!}{UogG32(xM%%M&RPe z1{HM|O*`d2<@V~XG9r5&d9E);dajtovm;cwobbijzGWKcvc*G*eNN*Ep-pki_N7CS zS!r5F<(P&F*Gn{w_d+yY1)W`PJe!NNalQ=OUX|>wW>ICAupw!E$k;}cwg{dsR;=cE zE>mtXdN?Cfm%0EQJmnnY26yxUaw4Xj71-VN zd{!buxesX?3ylnXk(lXiAC2?lW(M(HS|T1Ec-n0BZlUo>vUFK$;#eY~KtW)8mcyA!i_jJzT}XP=I7++{j! zzUj8~Yulp=i_i^LuGBNg=93RM8 z6g63j8(P&clQ862#~ZGRZm=Jq&3O6#`q=Fyv+mA!>;d(=T zwY4T@;d#|>Ya5c18A@gR!XwJ{v>s;fn{>sisQ2~}IO@NAtGUBEIx=!0&8{!V+E}@U zl)l_JQ` z1e&S1ULd{ShQe7WZ5QS0A!`|*hd66@MEs@nX(BJ>8FE~HiXTM${@KyBx?uEw*6Kh{cZj4c}nEm6(47UxxcV>p&GwOBYN3*>B&3|NWd zqIg{NyK0(hl;iPIKU*rW&0$e+N3|{)zv?SEdR+3Qh_R^>SCVU;NhiV>{j4erjkvebs^;$Ldm&5hM9cZ_(tPW@cLSwnErnxr?I@Vsijf=A}D1{kk6Vq~W*p0yI{*6h7 z?H`1%CL@9;5zlVgZDeR9klGtvc)FAu?vcmVl!tQXp$BKyhvF-Fi;hVSv^Pc^NVr*s zWmBo_SM%l(f`#65XbOnc%iY6b=U7F|3ngivLQmn~?FmmcSnKAuh-FUYq~&xqBlRCu zXzUtnl3=B9Q%`hJS78jgq*8o0-+hxvp<$C~f2uI*^WfE;pPTX4t3;E;K&vAf>Zk}; zG_4Fs7XJ6JfM0fw7VOU81eb-Qkqx_%y%oEYvz?=ng&Dhsf~c_*WZec%x&G6Vj8tmI z=oG7s1L~gYEWMWNdgqfh!M9`I zH<2ozJPA58COYFMU8%jAy`5~hSCeRH8y-N5)kRI@!NNorcSGu2Wqx!q{hpDkncu_4 z=S&XR5=H)cBnf?{xIrno)0dTQ$Tw~qc2X-G)euNsmrAQ?Q&ze%x=4EAu#s$bK}#xS zJa@sDyFI|RY>mTnhNfwmJLxAUs8*LJD#3g1gP|gS7Kso64Gj(9>K~k7gwF{G8>Fqw zf2B-8D*bY2MF~qE^KE^A>;X3?p@yeB^^s_c(? zo$@->VaQ(fdr?)Z!qxKf;<4?$xdoypcsyVF3}2A9&$N#-E>)&?p>(*mnm6BjEkLId zuHu}-!FfKJ4V5=6M(9lz4+Bz()@S@7+pD;B^OE@x3;uG|&F&E6Gt%#(3Gzy5>O>j7?6!*qZkFCarpiZ$=JVT{?w3WHdjN$#D zoB8b=U*vgo{Vz=cA4*H#S-A8GlH$7_UQ&Gfy=!ERj^NgkZLW%_o-h`rHBESGC2K)E zi~QFu!VXE*;!fJsaUT>R#k0sAoeC0o^6EBZ%wI&zE>_AF1>3oP9B2z0!fD>w3%`&7 zNo28Zx%9E0K|ojl1GW$@pDYA9d$WJ;qKH%xk_S>qeKhct>_H(XIg7XoKBjVIWeC~_ ziI5wQVzhY7YsyRAKG+gjA4n4Gp%Wh>_-Jg<@v{(05-+xxAg|%D%?L?BsCPj8Zn? zn==}-N=acaKC%0%#}GJ08J1v?7t2KaI6d(;dzC6n{5DEQBhQvGlV-4r#?ZbLt*l*( z%+iPQ$bFtSS=1wXpWaJNV!jGd@sO`q@5BfT^h|c+kR48b>PU^_5wv(+is0*^ii>pr z4L&tmEx}bkQx=W~{v*DS-17?R8pW6Lz65q!+v69V>c;>I)<$2pO z7o{NCW1rrwZJwg=Fy`8;_P$MYh!EOtOLDLQ0W885!YM34(a!ZRO9%~>kC@<;qTPIt z<{2A;D;4pcCpSNYfx7PNMAYvdNoJQb0(H2f4279i*#PFX(>dwTY z79+(&oa+g^k!SL*DlZM4t8A8ikM-gjm@ zJCs~!S{koMpP*OsQ)bjEo=#{WiX!&=95E_|t=@;PMem|dGFQE|{<7`ubjcX{oug=a z>jR_PUuUx_oUX?(QuSX#!PSbDx<9_C?*FLgt~bWqRc14DVV!L^8W+|i-^z+&clbC z)S;NtGOi#pp!qo@#FRP5xmsI6B7-{x&U-Njs)h{&Ho>4J+*`s8DjMX)O(i9(+&}g= z4-hk>b_B2JR9&30ow12`)OlePX?WMWWSHOP5?-Mqqo-;jDPD5=pf<1wm8>hlNF{n7 z@rG_*H67i|@f#GvV}3nWeGR3JYl(@<`n&4+2C3U*-LpePGz;|fVUmK!4Qi`DKs%ALJ3r>Tf7x==fiD0lywaoVvb-zBNMI1Zal0d+aJ$f{%RIb zxpQRM`SMb}7Q`ZWX+p33vOgE0v54Ouxy0S$U&S>9j23L8LpFz{H(@zA$PrP zyo~+(mDEeF9}h{c3IFUQRuO!4x_Y7{PDob%tuR{|S^ttoq{;x025#xg#j4(J>4>oR zbV$HEg;mH)U@?ms;uwpS56RMh>}#NKmG1D_%487j#@Wc}?x$yD48zjc8PRaz{qpq? zT;EAj;$d28dZ9gViRT$V%TlFSr9s8G?X6lRv=M5;DUJ|s)TV(|DctJdD>3^ahNG(n zV(-qOO0jwvWoSO4=@+6CFyXM+t@R7s#7T<->8cYtou@6hh z16$Z@OZcMn15ACB>^@eO(U9#0I+m>)Y=ScE*_oumNJR4(hEipH+jH&YTD$dke*}Cu z#5S7qVLq_2{yJOr*ohavkkUpNhq9C^Jk@AoQf*NupNE+&m0NC!od znMF)UhTzEx@`a&^Nube)8DjfE+>?Nb{51k|y^iP&WeJ$>303C>O#t0Tha4BXocJzB zY-Xg1FBG4?dVGl&Q51U}-q{^8=p57*8@N`5k-$%-GphF}=4-ZaNz^3+v?-!0hr#Vm5ivp7??x7C!NNJASB=bQ}*9%oi~|j@&(v2Svt3Q79fdak|&2 zJ~QG9X7&H{txRdD_?}cIPu6=$BV!3mPq7&}v9A(A&r(M=F z_H}4y-O72s==h5>vae!GeW@_os8w{4N{v@P09&Z8$k_w7aA^FL0b&bN;cm8& zglQhGEu6wHY1-bgwF6it)<#ZF|MN@q=8>V%q(mL^Xfm@MME?E~ftA!qUmqrxI6rSF zkK!_N(>TDpE(Y?ZjrWiR)x4j7cAto*vZAINm)&Rf3WQaieyRY;rNi=A2i0fBm&48c zKca$g-QXgUNu2fmP6DY`{=k+TZvIzlqNIEN>SZqIR=0M`^su(k2iTtC?dC3Hb+jeb zFj8rpy%3y^rxwV0%Tq9T9GqN)z3+%+2b9RiB5mlthDtMJ_Sz7s&@_hxqN9Pq)n6?P~F9X*=4vprUP z%NvW~C6+h6Q4Wlv7R}2ds~%HFEWI*q{?jdbtGdMwhZ>g=a)R4Oj0|gsA66q~lN{`? zClpe@@IkR>sP5yO@RHlCc7GLZbOYR$pzu7Ym1OXXSLPEPoe)N2v+}a+cr1+vXG&5% z56mC8t=(Zf`pq+y&k;32^4Dc3tN63Dz;@hP&gJL%W~X1Jg+EVwL@Eh^y&Hr=+WL97 zIdyyd7tTph?j{)Y2JUoH9j*}Gw}I&ssfur zKCLV3rRrD0kA8gmyjJr{_(A)j#||o9nMz(X+mLqaRhe5{o975-uEZM6UZW?x^yA0y zt9#BOy7Z0;g$n28;&FyzC6XncA|ewMMLG*Sm?V^u!5GP4Bs!;lIr+O>RyKxnR-y2{ zT=M&I+UNXw)#!vSagX{Zk0m@P)D2nB|2kT#J2Z#5lV9hh%E>J7eAb4foaYsz**SFz zR1`P-Tc|^Hd{a#3JHypQi>uST*HCPbs|FY1JaSj>E~FY?8PR!KSYH%G)OYbRy@;(F zR-A9vIaN-LR^|{P(s#IgoCK;F*P1Jh?Fdk;>QTabG|LK;J|ceW{PG>~8$sTAgP>(aVZrJ>i&txhj}z{qk6b zl@c*$7tW368zux?f_pSC@B~@EFk(!63fS5o*q~Qy`;PRPr=(eN;Y*sqt()8g$UPi! z{LJC1^(Y**Q|_4rA3Q4b>J~ZgrDJd#*6yWj_j&d5Z*-Rzd_1BfBONSkReyA&b*3l% z2IhR>>z6$NFUOnR4JSyud0dmr-`pnIreNC4e2vlYhF=#oS{1AMYK`69QcPPVKOAyw zr4}c-^Z8PiugV3ca2?Yzr9$SMMMJtq(DFX5y45Abe4p#O;S}!HxzzmW$40?renQCEvKg}lv?cK@Q)19klk z?bW@0`Fo*%P>yvfGMuvs}{DGXiS%MpYgx?me8f| zR{c!z{7B*i8}MbyjAROf_z`kbWa*V?P>@uz6};0j%@B1nXU}}_Te>B&4-VU`PoM;qXZU@zCBK_E z$o;SIv3}r{Tn{^3xbKYys@sQr`vm8H$Zi*q?+-82uxD{y>buofvRtJlLJ@MH)ak_7 z$0;*@G53!16nRPxhe%=vb1nY6JI^VrRr1pNj1#jy4AuB8}6d{`fNE`%`1J3RV14F`8bOaSM)p@IPE zaAl>q0|D{CHXF_p793wY#=ScOU+=yAs{OvQfbJ%H;nHKC&FUX5!Bx8}XKmd{Smzk@ zI7^_jW>ZB?h>Q?G^+45oo)GmaMd`dr$-1*w+7LRw^P9}F=Aq5I!{2aC z+@@CcuH|v9503k~zDiJflA?rYlCtiMm^PH;ylgGn{8>(VJO2xU3C`3?2>5TV%d}bd zBrat%?z79>mPXx)y66y)Cq=H-s?+egvSO6y(*~>HV*S9{G)qhF&4-mOJ70L7=*K?B zf7hnPI}nv(wjU_mGifpS!*Y1<)6hjXohLhHnv}%4I$p9QI%%_E75RI2-mkuHbHfL6umk964q^Yh+n-xXZPg@beUxE;rUgL z)B06O8+&Jue`3D6f%>orq0mHpe!L;;t*t!&y;x#eV@VNGi^`3dq?a2i^Dm6VYDV~| zie3c3Ga~Z*{$|4^U6%Xd zSuIvRdM`epKkQtvA~qv>rNP2w$~^i)&t_)Q*pR-20|Hk{^G#|~ zs7wvlv>vB+iBHeoOtZVh-LoPfo?)9zaKIO>gfyaJ`u z{iyP=EYD z|N2*Y^>T3@@v}1h({&Axai^^hr6tIFZ4<>%^L_8C&D~pJ>pHuCy*r1r5}!ho5l@bO z`*IjbsQ^ylzyy(sL@2^xaQf3u;r+FVVv3$)UE&b zy{aDVDfI~{+*?NaS0qHlzJ+|dpIgLI0|E#R0UHzutF?7AvHZvQbgJuP>*G#cKHV2E6N&z`Jp zB}jDdVd&??z4TB)g6kYvNZxnJzUARyXFZXKMepp6MattiFP|xAPc6ciHuZdAD~a_? zU%xs-|hi#lSVCJb1@k35E z3yXV&V$`*+QY^?Mh`O)XF6OGhJ6z2Nqz0mEIXTDVZ}NwU`F{JNq z*}6`=d(z4VBunJt_&}k!zfOua>LASRS<)J|4-TF~)5b zaZJ`TJmVedTAp~c6sk}Cc6z}}J}-KH>Q3eL89gE6bZy_nr6s<2H~z@iZ{pK6-SvXf zUZQ*cQ1l?!&HBFjxqiO6>`Qo>$*s`A+WbWGe9HvxueE1V-&qMy_AmQu)9CNId&Pd` zHmvVS*=j(Ja9WO?vgSRM=j6!byj#HLa`$n#P^0TB&KmWQyNBb=5|DhuX0854O&!}Z zZ{3P$%?10Z>p%02nkzMS-qj%A*u_b~R(f`w4qKg}*3K={7LETi{y+H!hgg>kqf;Bf zO*@9ALvwN-gVvd-o28iBG&q{CM>6`;95)qwWv<@M7F3(P&EKYXIhAQ5#1Yabo^*UV~6doYBSdehw^}D>uxHQ$uCx^OF*T_|dPDDTzMH!XhH` zMifUO;MiLGxD_G4_uR2+r`1PMS4co%Qcn>Lw|Q^OA3-&`M@K($2eD;=)^?1a;!OU; z12YRk^(tk}$kx_N6ijqG)b&wiUo6C*`WNM_T}*#Jgw6O>9EXss&Nx8sjSsR%YZsqq z+H8vG(p`W410M&nqYxQfKV}Xj_luT0BBcJMZwD{dt#Q^?@QmZtK22FeuVqMCyIfn4 zvPM>0C8cV?S@}@)rgV5DVpP`K3`#*sWQwzL^Dahr7m3|a6?snlpS&T*YKJHNC0?KvWSeiWVq2mSeo%cv$OLTef=;TO@}qZtgN7$`Xi4_j@%pRxGP zOj=pMdh1o@QZ)9r>Nzi8r%RpElbbwI5gC>XrLm7x@4o2|M=Q~~Ig8DdLgs|MMSNRU zTi}eHz~xkZrvicvd4}0RU1^)H1f{{0mrwcJCeoA!+0)R2U((-pyRj13vXbjyJCLT- z$DVdM_$AM6H-nXsY3KD1llR;NvTq4+VW(l;&UjL=RDQ@6Bk;Pz8x^Oa%aQ9`(QB4T z%eP~#M*{d3Hy)#Y z7tFjr_Q=K0)Y@T%|Az6sFlN6d{8HwuCThK~1K#b&*7k3t9H|*NxBL?FX}!>I8Fiau z<9zt&)M}O~)V?!~c!UT^JwmPv&JzG<8w!{mNXl zKnzA?D;s=$HPJ#=!jg{FHyW!F--d9KxW{G3bRWdDW{n)pJ@&5TdQm%v>LcQ#QLr@G zN{`FSn$*w#>eqLrT78bbEVBA43MTu8Phlo@^Rj%^nKa*I4Zqe~Qdr6B*UVV;_R>=! z)EWK;c8;4&GesK3(`~*RNcnPQ<}5$*1-El-DGh7Q6t+V0ba&}eX5Y&gR!^)UdAE+N z^5prpQt*g3N(VO{Ptlh)Y^?0e*PFKp zf4J(b)hAK1318Nx^}E2twzR37S6VFhyqKw{n7%GiQ!_$eNd1KbO-x>2v`IxdQ+Av6 zl^=bvuBCoS>K1hae(>j|Bq`dZ9EbN6x#)xhxZe3E+~2NCL3Uq&yo zNt;n0BAvd3jPFmqIjxCsnyRM~h-9bPT^q=+Y3UdWPX&86yQyKmyOPVz?@qZPDn$CM zoxkrhH)@u}5-SZk*C#y`1~pC#^?<@%y;<7I?Mq~qn$?n=Mm1C%j|Ld}sX+&x{>=pv z-RI4TQoTKr#9S=qy3eWRo;Ym^U2I~ULtVXjD`+Q?y)(JJ&R}AL*Xoe@$ju6gt@YMI zN6nIGq(I`Q$L@m}*-y6YMTV{s{m{6NXArn~OCSqNaC*V`B}J=X@T2lcw!8|;&qfJ1 zbL83I^djcRoMDY=>wW)Z9(;AoLs0IOrBKOE>d-Kucn z#03L`1m#`q#dvg=14G-B15>v~I;mO<=PTvd&Djbk&Zbff3+JH0 zM6rQDtRP`(Q>I$6KI&JRH%8Y=nr@i%ezPgm$+@+rH*;@r>JljhHmUP2r||1DA2rU6 znUuUl5qi$>#v6yHyyzm5up%9o)Pseg{)jIz3+nyPUz-LwU-=?vI;P%V`r0%j?}6?T z)px;!=NA?0)0NCKf=ff#mm^FreS9l;E9Rq*QL0-(T~S?L-3w2?duM|W_-^Fg(7whcX4&^-+3cfNw;F;f7&on+RaAYM)=zR5 znk{>O3u2kt%S)0{U{ebM+V82*pO#5`yk$bpmx00)q)qf|A&{VH;4}gHn`yD8houf!imvq^6?v7~%7o4n)MW0zR>BW|D*W{r*)}cbQh_O_*gFNiEQE*?yWXtwju2#Axp zT;jNI8@6@lcvcbBy@bi1*HG*jyEj==hNt?;DvaZUI@>mSl@;cb(Fg|O1lG8|0H(_a^L%C+E!0gzozs- zA(lC%=sUel(k{#72S59bdT;!d{eeyU{dzpz68-N_6-AX~r6kog*c7FHM}W8#zViv$h@iwB z3kDSwLMQmQzjn?2+$|^SVrpgg%e(vu7cEp)^#lB2G=QUof+GO0cs!iArO`iUVE;s0 z6E54r1V}YtK>APrJvRX_VWUA#)moWY*&Eq9|FhlpPvjY)k0hl4ZVe2&|6swdE+7Cn z$dIexkAL-|gC0a=KKt%;+Dy|bAqY=KpbRBc0c zA#Q^K4I6-dBJ6)L>rZO$T8uVT07~nC)S>kt4BSg|0<}=z-TM{RTmV5tHkt2uTV zKwAd`l9T`UXyd>_l(n^YafV~7kC&<|9tzHxy*{= zmB0uOE-rj)FuFf}R0oKI^q?SZxBnhXu!K3DsXtlpKfQ&mVE8jk6rn&3=ztnPy8{mb zxY)2Ew|(RvLb&_4@oEyC2nk=kQgTz)VKA5&E;M`{i$r|xbi9sPlE8)i?IXi~>O$}m zb^yeM?STPl5B~R9k;27PvU9dF_fRqW_W;+Qz!I2ArpG&QXEM0Jupx=`aqt=dfk;3R zIR7IQ{Bj~c5%L$klCHp+!w5OXo4uyTvp0+aK0NH0W14xn`FIl!KYuc&xTD=aI|TmZ z?lYDkv>Nc@klp0a4mbV+d|XE>S0fV-HJ5*vAAcfGYlQ?KFSo3VaFPEz%l9W_N(-sV z@r=#91Q$}v%G3--FekLHHah|@p8#}$2K@8CCyNpmCfq=CYdb=$0-*dI88@j;02DQW z9ceHEIK;~V>=qc%@oSg*1Xws$o8Gbc0ODZ&jtt)cGmdw)|GYiqD1|dDqwU4|!gIW& z&H}G{Jnl)3z+Zo#vH;LJ3kD?^|2;?`P#%v7+Xrwjpa~qW_}}27ooxO(*&L-1jm!?^D8XEl4a8&c`S5L?8=;feqWMT)nTXMF)zPommFtR zqplYNlw~lWz0?Fdd^{M6#G@F?dHnM24F^oX{~faaQ|N6K6!Z{I{vAcAuE0b4`zG{1 zky8&<>yCFMtX%MrA@yn(81J4d$t`@m5wTr`g$XBu_&#Flw*v-5@>fdf!wm~g+sN8l z#mF9(`!sMMDK`bn=vlBtg*GBl-s2%nOzd22oh6JsoHR5Pj@O}%j#tYCAioPhAv@s(#`Oz+M32Kyvik6XJF( zAnfHcMy35YC!kFPz)}AOgn%IJ1rrJmX@kh^Hi$s~%2EDYHTHSK#FMgfG`2D|HG{FX zBNG^|$GezVCKI650P>*UH#`jjtqo0G$Uyht7t4GvR?s0oMxyvebqxcppHr13A+}y)}OhJVZ+NCO;Uq^f-+@88`4OpF1A zH7Y3K2-Y5t3b%-R!^Nota>4}(5DM)xzBa;z`(FiD)Yi%dT$c?)K$A~rBy9mUDIggn zeZM^+&8NhN9W3d(ukE@4aAyDxO}u*B>A)ccGFu}X7`*1^&^VV3@OuG1NZfvVm^w~} zFKYu1@xxwsOp&dh;{e=0115p4>9BU6ZUS}CNw@Eav};r?9h@GivsY5IXGvE;JLU z2T#V7uyQgnax{gd-b{N#{Ja3%6o7-3;%`s$(8=I%IDxD^O@<6W{eUNerdodVWZ-`j zRv1PpAIi`00Ju33;5EUb@wcaJ{A6%#BP+;a|M+mkbQndP37`uBG_(X$rr<-vu2pOA z)Y(9+ zvUDm~bu&lT|7{TcGbce>!<^p*=mY?r4hr39^;mQWW8qiqUD!T#LV~j~@N>`vmwY-A zQq$JR)yT>k61k372{Ct7nmmAB0MMzR2(I5a79D;c043)V1|(}70|Q#aiod{xgQEz~ zM5`{%0TKlOPZ^;|yu5QPrmPL5P6K+4V5tc+-wP(C>C6V|74?@b7=wnxF$uu0mYBW$ z&pQ3_%(NOCCxwWr3ow%gic%d=5rT;cJBEHhx<3ywt{E_(UtJ4SnBfA#VQBvgv2XQ& z%ymE}wELPwI0GN`WOdyl?OWIc;ISY-h0dctpM?#6LR(NuA?6w6VB=>npnD43sHXsh z!wde(G7JQu2Bm;pXkK)o!AJgebE}ytoNgw){2`4K05<|UMhB%2lvr@U;i+x@LbzEK zVDBp+J7@!2#DNV7zdMTi^12{9;OyTn{3HI!m~fcOz4QsI2@tyrh=oo8oj{Ekmiqon z?8&;e81?PaCx9;xJOgy>i6nuIe~PrkqTA244gk*q!l8kO$xi_cJ6h&;=RM>B8X*qU z4RQh8f6s>trvwJKzyGUx$9tiiI!pn`>C+=HpaaHTs#DGZN2az8)zr;^HT!@y&}laY z%_-60$Ga6IC;leDqIqC6&=b(ppK=2D{CPY{_ksm70ni%i{{#Sji88^S0JcmEwK|HP z0**`pj?h8jwX(v;1EM@$gbHr_IrMs`6)+C&rD_4`a(@0UKhBnC<%_w0ltf#01St@=j5|^ z;sCh_f4NRS@e@$tt3%>d(VehkrDiR40x+CNTHdK>903gC6L1R9K21UP1k`_>0UVv! zC_Ebc0nkG*|8zM#il;<}FT>`m^{tNpQ$7RbfF?g)^^_C9VGb+NmgqIG9L>Q(K;VP& z7NHuaMAxu%u`!1K4w>Pu^2}fsO~543h5GGb(*3{80;g&*tT`pJ3gn;R4@-{S^oCmS)O*Df2TO!j}7a&E^{Bts8Xxb z&H%@u9nPTELgcUq26Q4Xa_6-0|GU5P&t-i(*Huj(01PPwLMO5>txpU7(|}=c-vxXB zEQo0c=Kwx*T4-wff5L~OsOKtLNd>_ij9`xIzsVQ;wEw@)@vp35yim7Qz7Is$e@mmt z;k1)!I>PAtEjgmwA8&HkoK6c4hf(dFwAUd4_X8LVP>dRIh7JE~uZ<|IUZrtZ04jv$ zB`~0?St_n4fkGC?F!)>$rHLH@1UmUwj`Y^;Bv{xxnU2CEwn6H846#j~C!zj!PxYTx zlw2R?1=)kJ4x$Kj_9^TI8x}$)>;iJyOgtH+!?p`Bq51mN`vgpQ%j_zB<17dV{_cL( z_`!#T!04(wglfk@x1H27paKGoW1#TigkN zus7ny3Er)Q+|9NI2K0)=4Qx$?$yW%K5KvJUSf^FQjMon!z<*aI4HT1M6`X23A)++bRV z{+lgxh;`rre6W$~x2G`W|A7C`(h`Q3e`-%TIS3~B3e*ESIg?ELKTHD0HCulo+W@O| z1S~KMbaL;OdAb=uFU|-qr^fQGeuZSR`8v#K&tF)39B>wEFoJ0qO)j!38Sv)bl@s>j<$B- zx_Wy{7-o@XjlB&q;V)o7ABa&bIo%ZM_C_|xC;9`QXHp=k4y67FZE|C!r^D8=vICue z6BsS4L)&M{W`H$+hkVO&*!U1(!Y$y~ImaqNSn_wsr>%ky3&*MDu1lX?0kr-d@(Zfr zgTf8@A8HprFalct4r)tv@L^#GHEr2urwTyo-$BjfF>FjYdGxmkv2{oe^>>&sX*vNA z4o8b|~Gu&7yLoZOtgszrY^qvl0+!1VMf?;J1i668f z5dj^%0CYrn)_*#53Gm4bXEQi;D}m`TH9G)*9`FY`teFn{U#5V~q5_Oby9ZzfNZk!O zcjbP0x)~%*t>B$kY(5ypa{?1s{58Sl*QcLA!^#;}f1dljboTMRxgWzPPXNb`P`^=t z&(8tR1k{}lO4Wl#;Q_;ew6JvydI{k2&;7Z+IR_loF$w2C)&pFf8=P%C^iA)7K-RdB zJ^*yZ{>l_Q*poT|?R*H&Zvp%92I~&!rZVyS6LH~&l9+BXpIAWgHlP@K3(u>$6OsR| ztQ>Fj>ON8A5Pg8u5ulmTym%rs9N7ix{m735B)# zCy2))!Y*<_;E-&)cp(|}CK%A3>JBLS97EuL#gSj9ijS2J1i43_IURz$3kLK$;48|h zkYTtK2sVb?TgMWhnKQt@Lf0Pxz{&uwDkvr_3NpC;gH>? zh;sJkv9cq_hKYWB3IZWky!`bmDxj(U*ngfsdWw5I-0@{12-dL9t31dhFSNjcKs)KH z#IUi%jZ7@fOpo8^3_(q5KDx#QUij}*O<`cIejGa?6vE-)_C+mAu={WcpU6`__h z4CtWGMh6e@gd!6Jnos890;IwV=`ljDsGI0d2MxPq0>SSeAhLk;>>*1(=xT-n=oTHP z0l#?hAL(#>A!V{E4#ZkPH2T9@xtLD`hg(&GkY4N0EXN0sA$?2eWlAs0snOwc$IbjP z6C^bn0NjD*z7gB0r+~vCnhF22T;LTT(uHAR=}}jj?NtCW2V5i80YA*B+t?CdLwtB^o=4C9(ICm`iNGcg{I@ z9lqWDzRw@ebDoF4-gkF)c4l^Vc0Na$jINB%WdCiUEH3;_fm#zA-f^fW;GoDq->4?y zSFLp&@kCyMBQU~ZKRl=?eww{yB- z6qmp7gcb=Z@kj$7Ox<_|I4n=7T;qY>S&8T<~SSZI1bp@ zZ;-!O-0AOHyVE89K5Q7i8$%Kls9h~1anBr=m>fSw(i4w{9Ipe^j z{(L!|!xsKblnU0KP*ER&n}+lqLoZ8` zVMOXP*OEF`0o~mZw~M6^GPHg1Cy+u3440qR+fP(rk^<)|vlzJ1hRu)K;Oyq&TJTn7 z!DI!n#AXyt+P{7>&~5^aTcVoNRA_#Q3DNO3nSHC>=VaA`5a_M z&oITy+B%X7cb)rj4hgp+gv(=rI;a|#<6~Y zgK+&}(zr|QwNwF0o?6nm9&g}UEe#2mA zXzXvxl{%mZYHgBmE^F4*kH5oE2V%kauCRQrk1DG4p|M^}8$X_0P&;UCL$+FDztlmM z^_w8rk`FIM%4eY5b9axo1zFqCzQm0`Vet>1*d(3B}7#0g9y~v?~wl7!O(f|Y9 zih**(p^f@y`3R%FmEtLD3p|>( z+w=s8A0T1r&epu|PC25mNe_vz?$g5)210~Gu!pz`eX&;xr|#lKuroq^icyiSHgP@R zCqu9bnf_Je{c+SMORGmW4oI<`ulmIv@Mg8Z(R4Gq6`ILq`Wi}=M1};KDK6EZ$sgI$ z^cq!GoW+lC_I4-baQ6OXifMnp?0PVX6gP7PZW(r*(n0MKH9RE*2b4JR^xdCD?`r~7 zy0qlpB#BhTqD)lp@BBY5_R}UeJL>ehKYit%7ncQixU&omb3*43?EEc*$_|cOkIk zmXfdnz8`l|+~jQCRXLVIugYIoTX!)mz+G5?y3C+>-4FsQTG`P6_mz)K{0EEE2M&~b zW%oO3ywF5zyudC~FZ*w2y!wn>y$}3nQ#Qz?`vTA?M|V7YEyEd7Q`cHSz)H}$p?g}= zi^E$OFE4Yc`A`Xs^kNDkHQQh+qOlMf1Bj>;1;;Kb$lz^t7? z3VW-~n~zFDCa=Q#@ok%l`Dm}0qBqa4Km1`eV{TQwa{m+h$_;C3Mni9Q(tN!d3kNxXif z(Z@Dae3jy%1i;p3%6Amp0yDsU_34Ukng`>0vGscl#a(;MN8Wj`r zphjsSSs10Z$!UvMbdlv(Ed)KM$UB=h0h)Sg@*Gy1a=OsYs79P2BlI?5I|pn8I{JGQ z(C6Uty67RJ#lWZG3U2(>tg{l_oot(6VIzVIc( z7wh5)JKDQdWCvis346yqXX$EcZ21Zj?x_8LAEj$m?G?m|n#OQ(mJUgbjUN;*JE~v* zs!%_=rYQ}H^Ih&;OHJ{DhJ=hr84)FTUq*8%mKYm472v+`sQfkFk=lj{6kVQ(!!Nz% z==|#k?i9AU8|DbN4YehXlspUCp1so687tfrE6m+wXgxg!Y4^d`pS_lS6rjD!6ik!c zN52AP~fcit`A;UcMI9)3JglG{v;>Dcqi6XK>b z9@7c@0e(Dgur@aWu85m9pWS&Hc|w>a!juR@(u7thWF-&SFh+L?iH#vX0eu(-M&BJ0AmHh&Ylf^pv+0xN=aE zf?J`6{jCtPAB4=0Qt|EOph33O_!LW+u#Yhfb;0n8ne>(bC7A`{gvLH{)UJsN3D96C zJt!hn*+w{@ShQ$jG`_xY>{ek8ft;;!b0U_`n^VK_!DebHg|G z3}#bHl%ke$g-3H1BL#}BUr=)L%%L-q@QZM*0~Qy8g#pFxY-+^t675KxSS zuJABwONb#FX$qIn?|E*zgIbEC|n16&>%|;L=%W>*u-SX zIp2C}-R+;TsKwxZxoTGGtpl&xs${bI-0oU|3OlIL1aBhpc0a?=%Gix`$G6Ywzs_L- zahL!PJI~^?Y^uFQR~DUwSw_OW^lPt&e!#7$33jR=oY2D+EGCKMzisFB_Xnsq6p;^h z-5@on+;kD{+y#?U!Xf5mIGXWlsh}aoaFuc&MWK+y?)Ajp8O@UuwP1QP^O zgH9YJPVDTAOGuaMh=I6O37@3Tp@epCa_GZ(5Pl|v&+VbldxoLqp?yY1zXh`ae?_yl z&8F+XtNjp(rP=mR%GZO~so@b{+p6GHDUSWq*7;6OK6)?LCL8F4)&-;IZaVV=69kgI zdi2V~JCkEbK;nOK?6NL-;;O-rNH7c}kI;`U zSHapbwU8u?9^3kM&&KW?2@_MFney46>Ou;JNtlz?dpvrBbOW}QpZ(HT>0>JF;pp>C z=dOoHn_|zjWrMB1MgW}b_;w2k)_Gd>btKkbNPCN=(yPsoik={HhoIXtq7EoS(riuz z5t7PuVJ~hpZ(jiKwiVn#Rb4}Mc#|0_$Gxy~g5p^%9{6BdcdSb`)|$_%4<1w)pVd2- z6eo}H+dVX^LjG|zy)moZn3XRRpxZYlsj%7by+w7wQmYcjL)wFG3FvtDhi_4~U}|Q{ zBBo)FBTAvb)TKCl8JA%6Zj(IEuc0KH$mTk@Rw*phC|tU|%_aDw8LIr!OMi>FuQ;vN z`>$R4V+q7p8vBZiZ`L7mTy)nVe+oCNex2}ODByC;%a6@z-Y@2OhFemM=*84>-PY2z z)pFkiN>*9P;EgWY?eO8J)E(VuL?7 zV@@LvP|@9ZJ2tsb^UPDJYOV%k9qscvm|8>cxwUHX)I5{gW0r_Ky?+c8e+r6{_^fsn zE^5fw;|{rFye&4&l0bc^;)Ly;NI6o5*iu_yR-u>`f8F#&NyAL`i)G2RGLhKIdWJOw zNjs4Eutj>?)i81<@%d_o_+~HG;2Uc`M2fvi+-^}wT)`#|1WTi z(^%#szNR=F2MpzP<@UGQOr$kmi8bdx*}I z_R2=i)EQqhHKBNzk|Qh-m8X%4PrO=p0nMo@=ES3&e|no{lHUvyRj+`7L;XRuMYB#b z`VOz0zqDodhk|!93#WkYI`}6n$V~Bn7+c=9C~G;L;IO;tt3M*29GMh1;U7i0#FK z>*U_$_Fb@eVU_r8=r5^;SOQTxK&cM-6+da+-Gp(EDa3gX5eT>8jmDXwk=;fUNArve zLn*qZx)pvtKRVG2hhAa9@f45RbkCK6xp0w1y=R6;(FBO56B)TPx(8?|4ZxMG%ycs} z($WJWX;x%&8XdEiYc%=$S%yhMqf#wce?*{S9cvl!bR#*u9?&s9lf@qxA_=m^r(or6 zxSW+fsjPDC)*I0f*G;5TImxh(43eZK4z>6W!UNop;gXru^Vpg*6ePQGROEZo8<#@{ z_9S%c1rmwRK2o|z1l>vefvIqoQcF#+M8v1Wb%`1+YF$W6KkWBEOYMVq%Y=UM?eg`% zUxuZHw|Mn8KKyQnKm0%{R-Y$1uYYQYC;ti%*;GqXe)WY^54?JNkGl_LyMTC?JME=- zqhnx+p3}urGAo5Vfp}sI4_8X16em6V;So<>mMJDj3rHdp6IHjZJ+={3s)i|XPa3z` z6p=GHRn98l(6J)hKvoK5JV!C@>z5!?mQv&{>2`^po|lB8^NFp?c?qukl@m*`@E5)R zB=?}rnYryWN9OqAsk017s)$p&HqM~~WFw6r*X=RIN9`|3ITZI({g3qBiOuA)4Q8A# z+TovGic8WB)9z-A?!DClY99c#=ZkjsXH#6xmXpf6>rW>29ljeAI|m{1xNh8u{|A}G z@V5E0+TBUStg|=d~)q0Ln_voHDcgC4iTdA7=u#=hy))zY4#9ePAQ#0oBfErYN+px$ z`GI*Zh|#LTb@{W&&2}}-$LmC$)k zEY}YYpIr8Xj=PkE$}dINV}wQ42PvWd6Tdg1DRhItEysnbCb{TpRooVzRI=*Cw~I7~ zessWo?#Nax4GpkOSPo~UgkB6#KRa#HyAYsTg@T8YI%u0~f=`tw__5ZahLbRwPK=6;!Bb*!5OsRZhQ>kQS_T1ft=QkpG}rSzE#^ArG3d}naE0L!JYTGd ztzVu?UglS5#v2v8gKrb&7QmEaMVpuB3zdDUob<8P#^#IbfpRe@`57$M$26tAVY672 zktK%ZH382;@Nm0$C*UP`?C%f6O!w0L%HUAqavw(Dj|p*Vut}!;qn;&J)o|jD?MxE#ZcE93^XxBbeiVu>`aGj9u-4wHl>a zt6{xO5FxgqqpOPP_r<3-uYl5msmB|lbIfZv1nD1-hXKhJ;nz@T8*eC;(X%UbV--w2PuM)M znWVCpt%?=n=k2pMY0-XxcrsC$$4eJPXPTXWKio??s$8;SIQ4h!;p z(CrPr86BYo^I#$P`aGOwiiRizQ5luYx?jJVOYi#r!LJE3uS;eLIOrJ*K5^=JDMmtq zg%k8Ipp`E>V8oB~QG-kPHDJiAW-E~iR`}6QLd_Wx&|LBeZ>}zC=OoKuvFgi(n@FB7&Y;j@7#RO;?hwRtgu=by1}+w`sgte^l9)-zKIZ+YX6~b&(@1(SlkA+P`j8%bC;6>KnpWG@wC^Vd zffY}zr*~*~k-p9}1H@_)3d%9j8D diff --git a/lib/tl-core-1.1.0.jar b/lib/tl-core-1.1.0.jar deleted file mode 100644 index ac31a126e2544adacc375094b12f5d13c6b685c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17342 zcmaKU1y~))wsmlK2oNl|yL)gsxVyW%6Ck*|ySuvvKM>sAU4lD={LGv8-?_t``wIF{ zeBa)y`_!)L>RPpS%K^Z^VLyC;hW-##jw|xvt$_aV@m65o4rvi30a^(eQF^csa(^?d z^8r1>c{9L%JJA2#P+CAnLR3UiiB4MdT6%Ou3P4LYg#e(X8XujgS74ZB-rU>X1APbf z=Krs=g1^;m9Zdh@g#T`X@V_=r##Y9r4*J&rr!n$-V<)Tsuy^kEi*f+@@Zs$3yHovx zy|A&Pv4gq3mAQwph?}9Yos+q(4V|HtzN2Ggth8+J7v!LgKtTxkU7Qzj7DmXPMgMgM ztrv0e5d6=pa5g{ErqdK=AF6p`-7cUW@*|iFQT<~U$<9y5yRJtYo*!>tK{jzcCHmzJ zDC?Efnw(D$7coL~Da0P7{mgD#$aA#Y?iD_-sYG>TMm$ZQqyX#dRlkEM)=K*p#hN6Y z1P{>Jyx{lw@)?swwQ8y@-PGO}YYm$J6Znh-S_5}J1;wC+VhFp2a14pG0{HxJ1`&k0 zUS5kpEJ!+Gj)ctJpye$kQFs8$oE1dC_9zCbTVfF!N(~cXc_2lnuTFWk6pD7hUI_(3 z_nm1IrxcQY$B#IARK4pF(bc9-7{JjE&SfLICT>_&+I%3=;?R_?28Dy@J>sWEv(S7rYf8u> zWML|3aAbd4TeTntLP_f)CZsviIPBSxjR)4VPg>qtJJOigVRGYA^CM8?!mIVOka~{N zj6YjxjybX78i5_-c!NDa{=MyeaY$>g5I=l4C;9N6*v!to0!4`s>Dh*16Y)>$$7W?U4~(zL>smhp zvtYJy9u${6*+Oi$C!D;fL#QY{cwe>HdEWWxqguC9I|Cy(RjycE zTTBWrt|W0_dsK02Sy^uJebHj9nj+C$gEtW`S1em`GXSC=<<+xfq2&`=$q>Kl=a!>I zJuj9_@yH0kXq7rcJ2X!82(P>oizagwtpxS++=;A{X>OPT%-%?vy2X#LL) z7b_YUt(-Rf4VpA_p;{-Y?)vCY^A08xG@}oSP)Je0((mlp1j7{b3oD6|q=g2art((R zUoOX;tSe}pYchnCiY5{(Ai$^|rve>rF%bcUzGu?opTfdWO+B{@gqLiJY0-Smm^Q9zE- zYc~;DF&JZt)|7K$4lbQU#U~aTAK^Nf24%kX$ZciWf$n@v-p?)JW{ry?0H8U4B%g#D zQ|DQ%|FWoIK++6S#RJeVyLE z`{{>HPZ7R9lSO@qK!`vx*b-V7>}pTbY#Tz3i@u1N8!aa#J3qkaeq1jPkR z1_F_Fv<#u8AK<07UBFzoiGwcSwAa^-LwqTigyjO@^+ODVM$xVjRzaVp5^Z)PFE2U5 zfkxZ&EtL)3pv_6zXra3?@Dy9trB1&<;#8{dzE`dz>&xebD+ncoItU31Lqwv$6?6m_ z-s+;Ov3TO82}zO(&`=-)z<*pDtFlJ+kL?&%Y7e zOVTPRFQM}E+j3iuD`usLrrQ>`^)lk{7RT6gGRY*yQO>2vHnu3?@l38Q9V+4?|1eR4 z{^JT)_?S?Zx>8(Y`M!`T$ehP%bQ}&25)3ji_ms~zGCCR<)M}%g`@*YP#7cQQtlx4T zQ<{G6MwGRy9Irk)8-Dr`7$?zHm~wmHGOEoRs2KPqRA^4>!4oWPd8%^NZ$})C)-l*& z@HGT%EcHWG7Ppp6g6?Mb2*PcpqnOixqmPvn3!mU?@`1Z$ih82DsfI$9w{ZGS-&IcV zbm_@9kf~5d_)I;kjxI*VxF~^rHxp&Jc*KTg-_kbNV@tQ!7l<|C0(PGE8SL|BcvhfW z93E1Y@rkNGWSdesSq=`4J8j%lTK(>}j1I4Zi{TtP&(fR#t40qi9J*j2C!B4Mvm^w+ zM@?YKLVfk6MxmVNK|otdiLFDf4Q)u<*!*os;IfwTH$ZcyRYp)V6>iI%J(|n9l8J5I zC-HD!<5?q4T1`J{i4Cd>C$kx=mBkD(95OjTx+&-Wp0klWX9JpRhL$L2c(0!;*Lg3e zYa*xXEvG92rz^GN**w=-U6PiFwJ%%1WK^J2B&kzW`4RfstPPaus(3o$HJ#-WH0??* z4UT-0y913Uj*CW;HlV%0SVUF2Pd2yB6Rer2+jM46$FHct_{MgHE- z^2u>^`zJ2-?nfFbnpwUm^A5-)v2x2Ix!u7?A)r7Y4(q{E3&`o73f$ZZFiY&VfW|he z0_Oq|F#mPr+f|t&gQU<6?G8(r(ZyZM&u&MzaJpVU^3I;{iN-x+1NeICUBYmFNlgFtt*Z)LKFg~VTBVV; z25MGRuG%ggZJ7+rsoXi>?B01`bP~GJ=Se@%@kz@qhzYw}L~ z!5^s46G>7OGuSXDKixA0&iP3NS9%dQQWFM z)zb9@5-^@y_M4_GI~EL&waRZ?W5IXT0VwAz&0%Mi!-mVphCJ1q2!tG8jH(H$1$$sD z{1x!&1)cN(+xZvgt$XazH75MScVS}P6O|p(ioR;I^XgP8-KNYhqO|%i6LvbNQbUvWf+9p-eAbw zdS7YxiFRzqln`x!fDZU#ipT@ z2j3%phddo1J6Z-|6=7z_DWGPGjzKOtO=Wn@fX_f341-gbZK1J3hlxlwdL%jlPI;MW z=DsO)D~(Z}Wx6GkR;8*Y_ED6&d~1~$zIA;4`4R-KmN*^?W1HPB5T}8i z9LnTN_?55BwhP=1r)BeU**H{_2e)Oiwi~o`>)oV75{Ax~soMjv-1 zdv@2&OC7n@gjNq0Y%NsUVX^C3q3fAYVGBs(HlADkVsuFh${0~l`M~G-Fv1mL>QoSZ zvw;(~RsFea>mQ(9=njT_{e7poiL_B}+OQjz7bg!4vPVSv9k8-CTh8Rn73MpTRdNH; zhI6~7pshGV?$l8d&0T_uT?{R8*3cqOOK$* z65Q;iIn~Ze<=Z)u>%-sSw~l{Mbm^Rz>`$rJRcerVNQ_X9E(`~g_#=HvYG2zU$?xV} z^^MlgYDo@T6;pi#|1?0^_JiE~vUrjZ|4Z{jgS9i%Z=s~Q^i#K5fh8&|TCU2Qb#dl1 zLUNtK5T=1p^IIM_oqXiWPZovuEfKFN7#n*jJ)w8#&&0&`Z6_ z?XuNPy>Mq;OXsW)csI(fdpRu$2i^lHyGo2$bAHQk8B2D63-IchD!L(nt_F~+> zr8n|nI|Jo67LrDY>yRtHj_M3{+PrFV5Fc2!OHrE>e)T%@K~3FnTopzImgK4|LBK6b z)xI&T<#^sL_KJPkZc}*j5l8iBlCR+gm)r@V8^lC9&Ls`Yd?eJfE1i!&3i}*m;cjq$ z@kLIkZ7AQ_w?z<^-(35Bt$rNKt=6sQ=e)UnKBY(W{`K|QUnn~qH+o+@wfsUILp}Tg zna>pFsbw}`!HDBCsrm*Q1=TaBUtOIp8U@WWLL|Ltoi;rraNOQL!cP0EZVoMM2hrb?3VLq<)^TbV3nMe5AS1il*l2G`q5L zOj);1U$;)hH~0}>SO5iw^<+d@x&@n*E~a#7MMPNl1EJ&-TfSGAEQyA80+cP@?L3s5 z_1y8uluMpqymeOCd|w@~9`l-)oujLHb_sS)9rm$yQo)R`H%aiD3{h>@3)J7&%1%$Ygib8)L ze>xhc)15@JzmGhj{Dm(MY%QCUk(kEM(ea7~pMxEkZg5$`WjFXqBxJM~S7pk30kiK> zN#M3NDsjlgWpUXnOv!9$ttNY%tbvmNedRIRyo0Z^^!hQM+GR3k9jrxCIWmooMzMHF z12#-7;acSyJ6#hu*bK;t+zyd-US+Swo(ELBVgs*(%r3#4QT(gz?o&w-ptyFx%qE23 zKwQ705MX_AV$BJ!3BCWM>ICxlR>0cIp8>qJ0KpsIM)+S_K~&$$(fH4%@Q&G#1QkFZ z{J!&K$}Fu|Ptz7+!&1^op;iEGA%vV{gKlf6l5B0b>bJ&tDB%%GcT5#>2;xy<=F*59 zkHulSzw?}V&XBEZOYlLaCQ}UZ7MHE@K3GfCQ$-oUfe9`#LZMb;n> z160JdC<3U&14#UVeNdGc^3xZz3Ky5JXV2r4R?TCE@p8IRdPtPF-XC)PXdKE;juXEX zAr6?ARZ$JdWhzbJc6`%81qjSp5gk*MX{3Cr2~uFHiMYk~;_z<_yKC^R8p~a#*ATcx zO8ctwr5rG&vAz7B!*RjiP?dO#1kPI|KL6K9D812we+I%iW=yj03wq#$^%I(<&uR4B zNA~R^43^JH3g`mFf`ly+M=E62A+aoe!~L$8cv^>o;Fx^ANTf2;ISSNZ_v5@7=UMuE zd>iW^xcQ30h?SsC2~hZ*C5v=9(sIr_t1Pyn$>5(*et2vKe|t3O?HD54mDS$BFa=E=T!SteO=mb8bfhvFsKh~aiSXr4tKnVQvZ{iS#lgW8 z*o<^H%0>>0qIHT6+%ik*A5twLVt{b<4xT){i{N&dV2OT=YLYV?f+yl@tX}0PeyTrb zxJ#-DBFeq?p~*EVTuGSyP_DX`auaMf*)3Iq@5f_#yy<1 z;se1;o)lN{D6Ax()Y!m~^Ub2p4y+NKPh7vSiia*YQMcqOa(?Q;-y}O}a!|4%wT12b zI|lp}ElZu_Ed;1<^f%r=hr!*+_uRmfZGnT-N6CNl8t)5X=2Z3}r1C z_R!Fjak;@Nv2wEMPU&qNE_)o}oRn*7RDTby`Pp>=NZC$?p zdSwK;K$n3bvk=*}*A3FfzLRwG4JN}+T{LAFckjh+s3#NRYiq#@3Gi#gy7>|S&U-Dy z?>-(=p3RD5UFL(52^T~($D>M7~Y#@~dWIwv&Udyw$;WI8e+a|fsrh!c<>Lt$`O@lg{ zO@B5{yD5vbH#Y}ZI;s4$)oWr7GC46kY+}ald&n~zL0^c;R|^v8Cqlu5 zsoX^LzRI@n#i#(Kitm*gR$@x^wSecMZf4R{F!7A~L8YuxB9y2!f7n!|beh1gTxvwc z6c1)W6;FH|pc0x&)&2t}rvAHNd#dCfMoX!Nrn$g;8e2%a&&yhgM=AO!RMFZdi(jRjdZh zIPv+qC%-R*prU+mCag=Tot`P*cz@;9C2eA~ZFx%g`OuU#l?@P2VQ-uVzTD2RIqZC! zjnAk6DrS^k^nZm%as1lK_ze!IK~w#yG7AH+Rni!S1Qc}YJQRnZ9W z{=&O;6ABjJ$p%TpyiWA#HpX#dk~Lg;G`1YiO|1us^d%boMQwoiA@p0eB>nwxBfsKB z$v2?VXuQk|4^nhF2Z}5OM~(rZ%l?+c<5$VSs|H-&{9fYXY-g39?lnpx@%?6XR=ml< z2N*AcBK7EOn!T9l9VK}pk{UF_^cXLa0~VC())HSUH`%F{FEt^)qotrSJ$Y}1pfEkD zJlxtXLc=9AXcyQ{^G=0KoO5a;*=pq@vBB=TK0JYWkbGhSm&q!bG75e1$k~|r6oU0j zv}B5mDUzKoKhvD8f3rIWUt)Wo?GdzVhzX>)y$R6WNVF(cAz|I+JB5#T$SRoWQJo#P z-ISwx?|MX-{SpPI9UIBFTw(S_#%O^*`&Gaj&LLvQgNE@-~_W)cT~SpCz`X zTxA{S8>{$u^WZo<(jaDci>A`i(BvP#(d71+6%eB`-T3vn^>&b$eM(f;^zxTzl=$^h zhi%+TUYnMW!#3p5U@k9>w2_^?MpO~2w8Uqt$lo8%ca6{jv# zUm~GwlIVJGq~&`8vy=}SvN$16&L4uoUy8r^m_M*1N^Vxt)Udoj`M-|KB^0O@Wm`@C z(iiIUxu9!bYv~i{Bh0E!$w1X)q!2XgCCmWbOHB(7l5P>!8;@n-%;GiI&=RCNF{qU0 z^7j6cRWui?*H~Fpws-VJCLp{qsm>%($Z_`IoLS9MaRI6~6i2GjeMwD$R}+nsNEIz? zXhyh8ZU)jABO!>|j`W_r6DoZhA~WdF=w#{w!;95AsGuz{$nt`9-|%jG-j24)umW3B zMnS$iZq%)^e#TOkw{fE;%H7@LQgkVeLh2bK5o(ESjevFDX7i_5Q$lF6njfeR9#I=T zGG8B7oNIE^rMZy~@^y%*O4=8H?SZhyDH|>$s|&|eppq?a{e)rBVw_n*l+hPsaT(Fk z6@|1VmM1h3nnZy%OvZE9Ov1I1rysGHSWSHrO&Mm0cZ@fWN8DaX)XfcwLs;Vl?)kQb z?w9thmV0x>Jbm_4iDZTqoYO5?nZoOQ4@;(3al>H+K`k9w1;liCC?ZU{Zb;z)c`8p6 zuJ=+M1w~6DD|v&$z>$J(BUr5DhAT}Z@v3MX|Jbq4X(vunBwBB~NoZVw>BytcOy$Hq zGm&FSvmLRrN<}I%)4B8r+mhD=sFOKTK-=~P^BRf&Qn)+UjckiFG@zMwR0DmEZ*CdxDSi+7$+IIy3H`v0=Jis|Vrojcf^0{+>_1Ja>mMxpGG= zOf!b%t&h4VRyDzO;B-V*Glw^CGh83Uj|KQr8SrK$Mh~@1;gPWujG~7&hT28 z$HeT<3B`gdPHS(XqfZVv zWQa!Yl?XDIab`tgr1&S==6~54U4nyCdgJ ziUWxP#l`Gi%KzLV*^@(^g-^g_2tQzW8VOWO(6g$7m$J#7anfSlnxD$7mpUk;{Ym15 z(o1IRK!Rt#h)cumt^rSQ7jlR;c$dH|<%PH@iZHwtR+MR#?z0H=E zw2~3Aia43s{d=?$pp(C?ToYnzz9i4jLw8SKyVpe=y3@VyMv9;hSiRqCplF0$B9&}A z5o2Htn!eoU(`QyqVNnf1+9A&PU9$A~p?QU+V~**x1r%0Q2wml)?22EJd_pZ$Vl(@w zF;W@Uz|EPwk_%uo2&)X5`x`cKN^8IJ)^fPv$GzFaeWezI68tKftVS@1{F1G!rI#n) zN7i6nAR_sL$*rvz;P|8kYSYHnZXL=MzR?S%?iFM-agfwYD3ite#m=+u)g>>RAq=fO z{2*Gd=T(WjR*EMgjmCsZt1th8Atdm|ME-IcOJ;jJ{xM8{82Zz_Ecj7j=GX6l)|Vt(>fok2Ot56fUu^sR7%5@YrO(Fu-O5(%s`Z^({;Ulv zekUyg?2}K3XLpe;@EZk#(iyF(%+1L5)cSv$P;quA;^i?l;EJpumqkIu) z0-;O872*y{#~pxeNA(aVcUT`X5jiqreFMHSEr>221H>g9*BPgvlfM_R#0D=c$6LQIIr3w5ur=*-O;c9QI~OQcD792P9) zm%@H(zZ)f^dHmApycS*r5|!aRVBLCJk1cdz3g2zq2eIx-zvs$cCqs7d4QidrHA z(;OV762NCZrqU=iCT?_bXT@uFNgXHeJCg`n!$8tJ3j_rUD1`+Ya%^s-SKzWRFJq`Q zP)A4k&oDV&pn~Y`A7TvNQE|FO;&#X&uImNZ#`!m6ZLI8WX4>Ow{d#ri{%)(zX{O&$ zUSTH7&xNSL>}UtgNs|WJ^V_x84_3vo&@NYWH%Dc+x9>`g_NqpvWQSw%_0_?yatWHr zu_kRQOJ8Kg#R<1!c*t*4T2+b|YHv5^8?IEbXX0*781Cq2DCXfF#@!qfTqs>1AI_F6 zTDkt{HQW;xJP}XVy)GWpJ?Qx;ZkK^oc;FfPHv1yp>3LFX;Qo(LSBKgm6G0Mb=%(N4 zQfTbyPt7lwe}CBi?k6$V_O`yuh4}D+=pP@p#XQXIWDP8g4gdFf+gnjv7EJ)1cP7cY z{bNk^k7Vd=F}U83RC@h-W+*d@Ari5e_(L^#C)?B3O^Oh&k>`A7c~cLYNEZV>MjiF- z>XM~Ar{kk*juX5G<7*o}KChq`$k9$D49UXi*Y-Ri`>Xr}&NO=Roe|&;_B4znVS8he zdpQjQ-IDN~BE{L6O zZR3*%xt8{q$)X{>CC1iHpf(`WUr(y5F%4eP7+8s|u$Z@hWhk^fX!H1(I=9HlgIc6z zdN_QFiQcY*R%do?Qm=FiOBIOjF-Jy~G)$%5ITo(ZrD(jug$7aV@q;0WlHl@wwFcL) zm=uRrJ+^8Scv2QNe{%ij{h z9gnqLgk+G?^3BgimMg(<&Jw;awL;e9+GFe@U($WRytQbtN$DACd8MOdlJX{(|Lroc z$*S8DIIbwuN)yplYDMQPZuYtfqL#dDte3p{%+o~a6(5e^g>q*yYf@QYGGV^Li9cJK zFR@USB2|$N*tFVFLEw>_F}(W3E|{qz)Ecf;2Mj_m-zbtCl85TSOC07XoulUmdk8#( z50tRpbjcL+R7T8awVvzg@*IBXbk^|GmjoEbxKI)V5Qg;-W5GZ-!Iajt5v8R64jSSF z5QKt=hKht@-=|FTap@^=lLA7;cSO5ekwd}|2l)bx(lFC3;?WTHA{0RQ=b(RiiR?Ox zUHz)k85E+&@x!d_3*Lyt-J@xApAl+6bZ@(*HURHP2g&UwOD7j(90Y2B7hZK)~7d* z;~lgreDcCOVTJkHzzJDP9K+nW*%pqlK;S_f2SKywX{zK1_WPxSQo{z^Ri*2ThN0tO(7;I z7vRV{@C!5gaYSlEFfex!&OfC*mxTLF*DFFAS1L3v20FmXwZVoSTT+(?_Lc=svC7ry zI~Yzq{K9KDS4qA=UqWo@#>$%Or0__Ow-xuIK)C)b|A>Cc%LtQyJnXGp z!7;_Qd6_CZtFVftDSJ{-wTO$ihpL|(JmB{1CMg=KxWDAwFXGOf1%0vLcMkNg6g>|~ zF1#OzM+oeM0 z@`g+cr_pEMs7T;8_)`)|!>o#9K``O7lE`d`+-lK~+R^W=|dN@ID`ty4$(_iPw#c2lgqAO9Rry zPHr$asws*X)EZUTF9nriEent{zU?V0=lU&Vtx^>*i0Ona+3#Qw9YnV$pkXVZ%mWV7 zQ;O!e@sY-eub5gh;I7wxd&o7F+<=daBHWG64fzp#%x1#90LLPj$!x9v#Q%HOM33NP~m&1ZzRD)SNp-^KC40@Ji`397!_|zKbMc zTMjG~r&rC8oL>vR;3mEO2!MW?0*SaPO)Sp7L0(MLD>RmZV(_?eazhc71$6|a=jzYi zYAm`u|jHzk$>BAA! z)YpH5{(Y*uzf^))e~Ze=+knUa$Ei-**vZV+=ucWHQ_};IkDSppCQ@E;*9rmFky)Ko7xYblmtF>0DAl9#lIsJM#qd z#QN#4m)oCEzQt~4gyGI;I3E=j8)ABrLfgsCQ0kQTsI`d;0DELTi*UKOE?;5wy$j;FAKb7sk@xrT*%2tdmWbD|OiR)Rh^ZV(&?Ar>Ag zzuv=7yk^oRU3MIwjqX=!&re%`_f5uEE3q`c7)i+zVRNRm2&$!vmqj7E|P~b`&sgl#i(ujkRd+J z3H^onxx@AewW84C-rFbM*uCA92?p6)+-J4Rd%U&yeD`wR%nnhf=JN1 z`%2>gsE{mI{W!Aj4b8Y+g;Bd#VQu5^FILByT=BYHWhv9O8|zjifBVbzD&~kJp6_XB zt6Rz&t3P1Kn^iQ{Ub?9bq6DfPY7bLxJ*E^$`rRAGVFqizi-$PvYx^0^22PC823;2- z?UOT`dbajPZMtu;yQ<*i%_AEpeg+}1RdthI%+g%ANZ4}XYOAAHOmeJyMiI&$;1*uB z5<*{d(MYcQ$g6r`o!cGUu46DsC4kzO%Kv$&BZ)Rb>WCh${_3(-9j^0d^bH1&WpGx3 zkUjF$h)!Si1*v9XGfj3}9(+bGbl)ODCreB(2G|6H%_i=+B?Y!y6b9G~Q%4vkd@|y( z&+JDOUa&sOft?f^R*O6nxIvCk zp`gVjc?-QRC39%m-kKMA0G9lt?Qo0_qF1J4-1LN={iF`Fp)JxHnftzr^4pR+Z1agfeK zaw(K-`-c09RS@?JQY0nGp$b18#cq60<%d)mr!ZZTT|h(ljF);>r*CMryeV$wbZLD}6AAJv`r$PLu5(eU> zjsEUMSKNF`&PDhF@D;MCbD~FG7lXP`GzFz-R8~n9P}kuuPZEod>Z4O`1W2_x;3iDyCJWQnkV9=I3yI8`dXEJU}gDWKu1#$~~>@?)J{6j3GB)T<%vD zn|%oJw?QgbStbXs_3M4G>J1#^jF;am-}ZIoVMYT&sgpt^zQmJwKZrg=;52Dx3?og& zRDp`8Da^+SCu2kY$TW?KQg-`xm4bj>clIT=5nP!F%(xYVnIPF)(}l>C%Ca18X@+?~ zfq;*;A)?GmJh8FC*Fwcr-?;PcT};}I>e^r5GIU(ul5qZcmi_l=RK9KAXend7u1m$G zO6j)2OPj?vAT;zEnF~qgq6^j(RA#0XeVH~;gdiV|k>O5-VYU?v|0b*T(cLu*n=ks&jlnQJSt$&R$!H~t*6ZAppLpI_Ta|?UO zv+#30p%>yk;KYy84H+rajRPFdQ>dgnB_b+(k$gM$13tobEwTjz)kbvq_Q8PLcP~Cd zGFi!!Qsk^bOruR~6kYVoiV!fOE|TpLqAaw_j*#k*hq93B(1*4VOkppfes&QrK!3AH z2gNowQ5R1CJ?KMMke(;-s;VkHpPI!?9s%R$kNINQ6X-ez}a{01!abx>) z1shdTBbfqBzO+BT!gm273D34{vKcPaQa4kZ8uqNCs9Ulnv@`MhfjAt4)BrEdOwz%9 zVHMm0X0S0)9&Cc4e#rDlBS%+YkXv@__@uy`aNkcM_Z{~vmB-dV$ZayHsjsa3R|WN! zB7SK*M7MOihf6S$%*63dB(q_3#PiM0)>cDlvLbBboMdRpzzOMl+XaRdCO|>@!d^sf z#+0$qcCCPMIcY#F=yBURZAR;4s~o*l@G@0%ca9+Q9xRm%-^{5I?Gb@tn{ zS=on8!B2|75L@Qiek$!q7Vsx2QUuoEFM2w+g^rXKiAJ0#X{2@3P{9#D!(Q=CD!cnp znRb2*?LLTz02CvaQbX-YhlRcdDm{r*e)TA`#~Y&e?iMds!r;G6UrmV;)@oZ*{v{f| z8+M|0RCIRsvFo7Or8$FBJ(o6b@PwAury{$wBsQlG+LY=t$#~oUNZ)~#bYkqvs4j+= zs_o8+NrHv0kqM>%!)<;XwHS3w8%hHwAYH@N3|k3p8+Q)fYdd#dyLoEcJqs#{lg@4~ zw#1dh8Q)*zZjX%LLevUQs7!k~IRcpS`RkdP`kq#`W#`x=3GMct@eRmQn zxB{zkt;!Zn(4x2o)Y8|7Cg)_gT~_J|^pA+dX$#Zw6}j@jFonmvEm5O!i@PQ^sFjPF74LH$qohx z?ryNog^sCR4`l&VZI5Z+#S@$>x+M|nJ3c!%Y~h|6C0|PcacWoRX8R95VeEeevD7kP zu))-;^7Yo4tpz1}K2b2wuVU9p{y`egR8DY(MYkxXP6xH!pP_MhA`a&|li3e(L5#;g zD4U{oqPVm=DaU2l|9oQQ%Jnpf-7V|<0?)q?+x-zAk?X5pOO)=mDwmF6ag^>3JD1L& zt@8`w_k~NS7siQiN>9!rexnrNM1*z=hYx746+83~>P$rC<@59F^H-QS&G zpnlIy(bLhR1d>3C6ga_;92hSa?Z7HV9JXj^a0{RIOX8jPuyU;jTSB~W)ova@*>^^1 z6Y&Kgg)dg_K&}oM+NjYEz9e<6&pahHWF(nR%An$x$0I=iiyD{|ntrkXd&VKejgn34 z9UixGyQt#vo%nyNr|fen4)D;xbY_jiV06&*Inq!8bG5_!qF!vQopSdt=JyL`X?GZ&7g!CNo-i2e zS8XX37{K~av(ZP&LI>8Cd3!@PQy4u#Dq{ibX$Fo{$$+fIk7gzJHLm4G^}M!Uq`v2c zFD|>W;oxkdHoEzFIs~IVDDLe8_G6GeaCiZ1xkOcT>49mvS23{SN+wmNiOh5{> z2D0!qEWA`%BZMrWpYM7%F0droqVk>Sf8cM15ZLBr{9s4kpAf+id=v3L7N#eK7$9ue zW=+oYGzT>vHoRo?HAg)5^>|uOGR-L~g70U8?>G0%Z}GrcwL+*YUogDfb8mh`?@5&T zM%}nI4utr)3N%08@l@XdQQ`@@irkvyDB_vIn!n%dAG905pJfXwJ`+%0T;K>YZ&s6> z5h=}$jewCRwSb+=0I!MB4Hs8uKDLLI88FBYXw{I<$5o$+Q%TP&V(2lR2X99&vONa9 zb1~%o$)Wu7L$OA0Y%#X`5|#6Xc;LB4{}K`(i3bgrb6 zn69@u`T|9G*xM2(V#`fqRN>=E;=pXX{CPtOjha;9)9I9|F8+W-@Z^V zGq@+Sb+04|HG=N+*RhW2a78@7T3-v1pZ10B%aME!(YG|~TH@E{OHWyX+w~L+WqUfV z44mV{!$`1HH{)uRIbigk0P@&mLKE}Fr!I}JYRR(E5f%IIUN}VQ4qT{cN{tVG5Ytqi zk6QGKjyVrk?uy?08N&ENWhFv?`y%40Ul6il4n6un0Gg(v>fP}*rh5SXZpqmsQ2Eoh zc}V+@>FasN$Qep_-&~wGc?a+=5oG%Ph=q3xfr451P6-Szsjt%Ox@Q{Z#otwR{S#_g@B{aP)Zn=ME6Rji#yfn zTCZO`6Us=(S-f6K0CRM*7c&`~vUscH=IuH0-N?S7vG zZ}v^m5s8U_)iS(3Y#DlbT()W(gAvtTdX}DzvD0=40{?4vtJImPaIqVlB-v@r#|4t} z6&)}+00<~L*ndy*ej{n#3i5}a0)PDdSIYN$xA!UEe;EH!3j8SFKL6$RZwcV<5&p1# zTSNR$c@w(*Z~5cIE^qZevcCT>k3X`$|15uaq`g)D@c384>Hp>WM-KR(~B@2~0~vzOmPzE8vb3zGJ&k>5l9 zQ&R4G#P=fQzYz7&{uA-vCCl%T-V1&ILNa}OYry+!`xEKkWWVp>-m6vrg1f}{Z*YHU zSKp((7hV2^=7#x?Xn#pC-$T6@)BOc?^7e*=|NNeRlh(Zld%s!u7ns^xiu-?pz27=~ zkN18H<}W-2ihsg;w;}T$?mg}P7hL7ryA%HN^4^p1@A2Lf#(&`n(fkwMzthL>vEEaR ze_?6U{S(%|6OQk(-qRj`VPP=+6V`u{Any_1FWUb?EM@*D#D8DH%K;!D|FB1Y`x$uK LMc(24INTERNAL API

- * Initialization of actor - * - * @param uuid uuid of actor - * @param path path of actor - * @param context context of actor - * @param mailbox mailbox of actor - */ - public final void initActor(UUID uuid, String path, ActorContext context, Mailbox mailbox) { - this.uuid = uuid; - this.path = path; - this.context = context; - this.mailbox = mailbox; - this.askPattern = new ActorAskImpl(self()); - } - - /** - * Actor System - * - * @return Actor System - */ - public final ActorSystem system() { - return context.getSystem(); - } - - /** - * Self actor reference - * - * @return self reference - */ - public final ActorRef self() { - return context.getSelf(); - } - - /** - * Actor context - * - * @return context - */ - protected final ActorContext context() { - return context; - } - - /** - * Sender of last received message - * - * @return sender's ActorRef - */ - public final ActorRef sender() { - return context.sender(); - } - - /** - * Actor UUID - * - * @return uuid - */ - protected final UUID getUuid() { - return uuid; - } - - /** - * Actor path - * - * @return path - */ - protected final String getPath() { - return path; - } - - /** - * Actor mailbox - * - * @return mailbox - */ - public final Mailbox getMailbox() { - return mailbox; - } - - /** - * Called before first message receiving - */ - public void preStart() { - - } - - public final void onReceiveGlobal(Object message) { - if (message instanceof DeadLetter) { - if (askPattern.onDeadLetter((DeadLetter) message)) { - return; - } - } else if (message instanceof TaskResult) { - if (askPattern.onTaskResult((TaskResult) message)) { - return; - } - } else if (message instanceof TaskTimeout) { - if (askPattern.onTaskTimeout((TaskTimeout) message)) { - return; - } - } else if (message instanceof TaskError) { - if (askPattern.onTaskError((TaskError) message)) { - return; - } - } - onReceive(message); - } - - /** - * Receiving of message - * - * @param message message - */ - public void onReceive(Object message) { - - } - - /** - * Called after actor shutdown - */ - public void postStop() { - - } - - /** - * Reply message to sender of last message - * - * @param message reply message - */ - public void reply(Object message) { - if (context.sender() != null) { - context.sender().send(message, self()); - } - } - - public AskFuture combine(AskFuture... futures) { - return askPattern.combine(futures); - } - - public AskFuture combine(AskCallback callback, AskFuture... futures) { - AskFuture future = combine(futures); - future.addListener(callback); - return future; - } - - public AskFuture combine(final String name, final Class clazz, AskFuture... futures) { - return combine(new AskCallback() { - @Override - public void onResult(Object[] result) { - T[] res = (T[]) Array.newInstance(clazz, result.length); - for (int i = 0; i < result.length; i++) { - res[i] = (T) result[i]; - } - self().send(new NamedMessage(name, res)); - } - - @Override - public void onError(Throwable throwable) { - self().send(new NamedMessage(name, throwable)); - } - }, futures); - } - - public AskFuture combine(final String name, AskFuture... futures) { - return combine(new AskCallback() { - @Override - public void onResult(Object[] result) { - self().send(new NamedMessage(name, result)); - } - - @Override - public void onError(Throwable throwable) { - self().send(new NamedMessage(name, throwable)); - } - }, futures); - } - - /** - * Ask TaskActor for result - * - * @param selection ActorSelection of task - * @return Future - */ - public AskFuture ask(ActorSelection selection) { - return askPattern.ask(system().actorOf(selection), 0, null); - } - - /** - * Ask TaskActor for result - * - * @param selection ActorSelection of task - * @param timeout timeout of task - * @return Future - */ - public AskFuture ask(ActorSelection selection, long timeout) { - return askPattern.ask(system().actorOf(selection), timeout, null); - } - - /** - * Ask TaskActor for result - * - * @param selection ActorSelection of task - * @param callback callback for ask - * @return Future - */ - public AskFuture ask(ActorSelection selection, AskCallback callback) { - return askPattern.ask(system().actorOf(selection), 0, callback); - } - - /** - * Ask TaskActor for result - * - * @param selection ActorSelection of task - * @param timeout timeout of task - * @param callback callback for ask - * @return Future - */ - public AskFuture ask(ActorSelection selection, long timeout, AskCallback callback) { - return askPattern.ask(system().actorOf(selection), timeout, callback); - } - - /** - * Ask TaskActor for result - * - * @param ref ActorRef of task - * @return Future - */ - public AskFuture ask(ActorRef ref) { - return askPattern.ask(ref, 0, null); - } - - /** - * Ask TaskActor for result - * - * @param ref ActorRef of task - * @param timeout timeout of task - * @return Future - */ - public AskFuture ask(ActorRef ref, long timeout) { - return askPattern.ask(ref, timeout, null); - } - - /** - * Ask TaskActor for result - * - * @param ref ActorRef of task - * @param callback callback for ask - * @return Future - */ - public AskFuture ask(ActorRef ref, AskCallback callback) { - return askPattern.ask(ref, 0, callback); - } - - /** - * Ask TaskActor for result - * - * @param ref ActorRef of task - * @param timeout timeout of task - * @param callback callback for ask - * @return Future - */ - public AskFuture ask(ActorRef ref, long timeout, AskCallback callback) { - return askPattern.ask(ref, timeout, callback); - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/ActorContext.java b/src/com/droidkit/actors/ActorContext.java deleted file mode 100644 index 6034a22..0000000 --- a/src/com/droidkit/actors/ActorContext.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.droidkit.actors; - -/** - * Context of actor - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorContext { - private final ActorScope actorScope; - - /** - *

INTERNAL API

- * Creating of actor context - * - * @param scope actor scope - */ - public ActorContext(ActorScope scope) { - this.actorScope = scope; - } - - /** - * Actor Reference - * - * @return reference - */ - public ActorRef getSelf() { - return actorScope.getActorRef(); - } - - /** - * Actor system - * - * @return Actor system - */ - public ActorSystem getSystem() { - return actorScope.getActorSystem(); - } - - - /** - * Sender of last received message - * - * @return sender's ActorRef - */ - public ActorRef sender() { - return actorScope.getSender(); - } - - /** - * Stopping actor - */ - public void stopSelf() { - try { - actorScope.shutdownActor(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/com/droidkit/actors/ActorCreator.java b/src/com/droidkit/actors/ActorCreator.java deleted file mode 100644 index 2378127..0000000 --- a/src/com/droidkit/actors/ActorCreator.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.droidkit.actors; - -/** - * Object for manual actors creating - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public interface ActorCreator { - /** - * Create actor - * - * @return Actor - */ - public T create(); -} diff --git a/src/com/droidkit/actors/ActorRef.java b/src/com/droidkit/actors/ActorRef.java deleted file mode 100644 index 6a23870..0000000 --- a/src/com/droidkit/actors/ActorRef.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.droidkit.actors; - -import com.droidkit.actors.mailbox.AbsActorDispatcher; - -import java.util.UUID; - -/** - * Reference to Actor that allows to send messages to real Actor - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorRef { - private ActorSystem system; - private AbsActorDispatcher dispatcher; - private UUID uuid; - private String path; - - public UUID getUuid() { - return uuid; - } - - public String getPath() { - return path; - } - - /** - *

INTERNAL API

- * Creating actor reference - * - * @param system actor system - * @param dispatcher dispatcher of actor - * @param path path of actor - * @param uuid uuid of actor - */ - public ActorRef(ActorSystem system, AbsActorDispatcher dispatcher, UUID uuid, String path) { - this.system = system; - this.dispatcher = dispatcher; - this.uuid = uuid; - this.path = path; - } - - /** - * Send message with empty sender - * - * @param message message - */ - public void send(Object message) { - send(message, null); - } - - /** - * Send message with specified sender - * - * @param message message - * @param sender sender - */ - public void send(Object message, ActorRef sender) { - send(message, 0, sender); - } - - /** - * Send message with empty sender and delay - * - * @param message message - * @param delay delay - */ - public void send(Object message, long delay) { - send(message, delay, null); - } - - /** - * Send message - * - * @param message message - * @param delay delay - * @param sender sender - */ - public void send(Object message, long delay, ActorRef sender) { - dispatcher.sendMessage(path, message, ActorTime.currentTime() + delay, sender); - } - - /** - * Send message once - * - * @param message message - */ - public void sendOnce(Object message) { - send(message, null); - } - - /** - * Send message once - * - * @param message message - * @param sender sender - */ - public void sendOnce(Object message, ActorRef sender) { - sendOnce(message, 0, sender); - } - - /** - * Send message once - * - * @param message message - * @param delay delay - */ - public void sendOnce(Object message, long delay) { - sendOnce(message, delay, null); - } - - /** - * Send message once - * - * @param message message - * @param delay delay - * @param sender sender - */ - public void sendOnce(Object message, long delay, ActorRef sender) { - dispatcher.sendMessageOnce(path, message, ActorTime.currentTime() + delay, sender); - } -} diff --git a/src/com/droidkit/actors/ActorScope.java b/src/com/droidkit/actors/ActorScope.java deleted file mode 100644 index 80b5d51..0000000 --- a/src/com/droidkit/actors/ActorScope.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.droidkit.actors; - -import com.droidkit.actors.mailbox.AbsActorDispatcher; -import com.droidkit.actors.mailbox.Mailbox; - -import java.util.UUID; - -/** - *

INTERNAL API

- * Actor Scope contains states of actor, UUID, Path, Props and Actor (if created). - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorScope { - - public static final int STATE_STARTING = 0; - public static final int STATE_RUNNING = 1; - public static final int STATE_SHUTDOWN = 2; - - private final UUID uuid; - private final String path; - private final Props props; - - private final ActorRef actorRef; - private final Mailbox mailbox; - - private final AbsActorDispatcher dispatcher; - - private final ActorSystem actorSystem; - - private int state; - - private Actor actor; - - private ActorRef sender; - - public ActorScope(ActorSystem actorSystem, Mailbox mailbox, ActorRef actorRef, AbsActorDispatcher dispatcher, UUID uuid, String path, Props props) { - this.actorSystem = actorSystem; - this.mailbox = mailbox; - this.actorRef = actorRef; - this.dispatcher = dispatcher; - this.uuid = uuid; - this.path = path; - this.props = props; - this.state = STATE_STARTING; - } - - public AbsActorDispatcher getDispatcher() { - return dispatcher; - } - - public int getState() { - return state; - } - - public UUID getUuid() { - return uuid; - } - - public String getPath() { - return path; - } - - public Props getProps() { - return props; - } - - public ActorRef getActorRef() { - return actorRef; - } - - public Mailbox getMailbox() { - return mailbox; - } - - public Actor getActor() { - return actor; - } - - public ActorSystem getActorSystem() { - return actorSystem; - } - - public ActorRef getSender() { - return sender; - } - - public void setSender(ActorRef sender) { - this.sender = sender; - } - - /** - * Create actor - * - * @throws Exception - */ - public void createActor() throws Exception { - if (state == STATE_STARTING) { - actor = props.create(); - CurrentActor.setCurrentActor(actor); - actor.initActor(getUuid(), getPath(), new ActorContext(this), getMailbox()); - actor.preStart(); - } else if (state == STATE_RUNNING) { - throw new RuntimeException("Actor already created"); - } else if (state == STATE_SHUTDOWN) { - throw new RuntimeException("Actor shutdown"); - } else { - throw new RuntimeException("Unknown ActorScope state"); - } - } - - /** - * Shutdown actor - * - * @throws Exception - */ - public void shutdownActor() throws Exception { - if (state == STATE_STARTING || state == STATE_RUNNING || - state == STATE_SHUTDOWN) { - actorSystem.removeActor(this); - dispatcher.disconnectScope(this); - actor.postStop(); - actor = null; - } else { - throw new RuntimeException("Unknown ActorScope state"); - } - } -} diff --git a/src/com/droidkit/actors/ActorSelection.java b/src/com/droidkit/actors/ActorSelection.java deleted file mode 100644 index 2f65941..0000000 --- a/src/com/droidkit/actors/ActorSelection.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.droidkit.actors; - -/** - * Actor selection: group and path of actor - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorSelection { - private final Props props; - private final String path; - - public ActorSelection(Props props, String path) { - this.props = props; - this.path = path; - } - - public Props getProps() { - return props; - } - - public String getPath() { - return path; - } -} diff --git a/src/com/droidkit/actors/ActorSystem.java b/src/com/droidkit/actors/ActorSystem.java deleted file mode 100644 index 02b0d96..0000000 --- a/src/com/droidkit/actors/ActorSystem.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.droidkit.actors; - -import com.droidkit.actors.mailbox.AbsActorDispatcher; -import com.droidkit.actors.mailbox.ActorDispatcher; - -import java.util.HashMap; - -/** - * Entry point for Actor Model, creates all actors and dispatchers - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorSystem { - - private static final ActorSystem mainSystem = new ActorSystem(); - - /** - * Main actor system - * - * @return ActorSystem - */ - public static ActorSystem system() { - return mainSystem; - } - - private static final String DEFAULT_DISPATCHER = "default"; - - private final HashMap dispatchers = new HashMap(); - private final HashMap actors = new HashMap(); - - /** - * Creating new actor system - */ - public ActorSystem() { - addDispatcher(DEFAULT_DISPATCHER); - } - - /** - * Adding dispatcher with threads count = {@code Runtime.getRuntime().availableProcessors()} - * - * @param dispatcherId dispatcher id - */ - public void addDispatcher(String dispatcherId) { - addDispatcher(dispatcherId, new ActorDispatcher(this, Runtime.getRuntime().availableProcessors())); - } - - /** - * Registering custom dispatcher - * - * @param dispatcherId dispatcher id - * @param dispatcher dispatcher object - */ - public void addDispatcher(String dispatcherId, AbsActorDispatcher dispatcher) { - synchronized (dispatchers) { - if (dispatchers.containsKey(dispatcherId)) { - return; - } - dispatchers.put(dispatcherId, dispatcher); - } - } - - public ActorRef actorOf(ActorSelection selection) { - return actorOf(selection.getProps(), selection.getPath()); - } - - /** - * Creating or getting existing actor from actor class - * - * @param actor Actor Class - * @param path Actor Path - * @param Actor Class - * @return ActorRef - */ - public ActorRef actorOf(Class actor, String path) { - return actorOf(Props.create(actor), path); - } - - /** - * Creating or getting existing actor from actor props - * - * @param props Actor Props - * @param path Actor Path - * @return ActorRef - */ - public ActorRef actorOf(Props props, String path) { - // TODO: Remove lock - synchronized (actors) { - // Searching for already created actor - ActorScope scope = actors.get(path); - - // If already created - return ActorRef - if (scope != null) { - return scope.getActorRef(); - } - - // Finding dispatcher for actor - String dispatcherId = props.getDispatcher() == null ? DEFAULT_DISPATCHER : props.getDispatcher(); - - AbsActorDispatcher mailboxesDispatcher; - synchronized (dispatchers) { - if (!dispatchers.containsKey(dispatcherId)) { - throw new RuntimeException("Unknown dispatcherId '" + dispatcherId + "'"); - } - mailboxesDispatcher = dispatchers.get(dispatcherId); - } - - // Creating actor scope - scope = mailboxesDispatcher.createScope(path, props); - - // Saving actor in collection - actors.put(path, scope); - - return scope.getActorRef(); - } - } - - /** - * WARRING! Call only during processing message in actor! - * - * @param scope Actor Scope - */ - void removeActor(ActorScope scope) { - synchronized (actors) { - if (actors.get(scope.getPath()) == scope) { - actors.remove(scope.getPath()); - } - } - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/ActorTime.java b/src/com/droidkit/actors/ActorTime.java deleted file mode 100644 index 5d6ceeb..0000000 --- a/src/com/droidkit/actors/ActorTime.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.droidkit.actors; - -/** - * Time used by actor system, uses System.nanoTime() inside - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorTime { - - private static long lastTime = 0; - private static final Object timeLock = new Object(); - - /** - * Getting current actor system time - * - * @return actor system time - */ - public static long currentTime() { - long res = System.nanoTime() / 1000000; - synchronized (timeLock) { - if (lastTime < res) { - lastTime = res; - } - return lastTime; - } - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/CurrentActor.java b/src/com/droidkit/actors/CurrentActor.java deleted file mode 100644 index a2eec58..0000000 --- a/src/com/droidkit/actors/CurrentActor.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.droidkit.actors; - -/** - *

INTERNAL API!

- * Keeps current actor for thread. Will be used for better implementations of patterns. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class CurrentActor { - private static ThreadLocal currentActor = new ThreadLocal(); - - public static void setCurrentActor(Actor actor) { - currentActor.set(actor); - } - - public static Actor getCurrentActor() { - return currentActor.get(); - } -} diff --git a/src/com/droidkit/actors/Props.java b/src/com/droidkit/actors/Props.java deleted file mode 100644 index db884e1..0000000 --- a/src/com/droidkit/actors/Props.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.droidkit.actors; - -/** - *

Props is a configuration class to specify options for the creation of actors, think of it as an immutable and - * thus freely shareable recipe for creating an actor including associated dispatcher information.

- * For more information you may read about
Akka Props. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public final class Props { - private static final int TYPE_DEFAULT = 1; - private static final int TYPE_CREATOR = 2; - - private final Class aClass; - private final Object[] args; - private final int type; - private final ActorCreator creator; - - private final String dispatcher; - - private Props(Class aClass, Object[] args, int type, String dispatcher, ActorCreator creator) { - this.aClass = aClass; - this.args = args; - this.type = type; - this.creator = creator; - this.dispatcher = dispatcher; - } - - /** - * Creating actor from Props - * - * @return Actor - * @throws Exception - */ - public T create() throws Exception { - if (type == TYPE_DEFAULT) { - if (args == null || args.length == 0) { - return aClass.newInstance(); - } - } else if (type == TYPE_CREATOR) { - return creator.create(); - } - - throw new RuntimeException("Unsupported create method"); - } - - /** - * Getting dispatcher id if available - * - * @return - */ - public String getDispatcher() { - return dispatcher; - } - - /** - * Changing dispatcher - * - * @param dispatcher dispatcher id - * @return this - */ - public Props changeDispatcher(String dispatcher) { - return new Props(aClass, args, type, dispatcher, creator); - } - - /** - * Create props from class - * - * @param tClass Actor class - * @param Actor class - * @return Props object - */ - public static Props create(Class tClass) { - return new Props(tClass, null, TYPE_DEFAULT, null, null); - } - - /** - * Create props from Actor creator - * - * @param clazz Actor class - * @param creator Actor creator class - * @param Actor class - * @return Props object - */ - public static Props create(Class clazz, ActorCreator creator) { - return new Props(clazz, null, TYPE_CREATOR, null, creator); - } -} diff --git a/src/com/droidkit/actors/ReflectedActor.java b/src/com/droidkit/actors/ReflectedActor.java deleted file mode 100644 index 775b90b..0000000 --- a/src/com/droidkit/actors/ReflectedActor.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.droidkit.actors; - -import com.droidkit.actors.messages.NamedMessage; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashSet; - -/** - * ReflectedActor is Actor that uses java reflection for processing of messages - * For each message developer must create method named "onReceive" with one argument - * with type of message - * For special message {@link com.droidkit.actors.messages.NamedMessage} you can create method - * named like {@code onDownloadReceive}. First letter in {@code Download} will be lowed and ReflectedActor - * will use this as {@code download} for name of message. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ReflectedActor extends Actor { - - private ArrayList events = new ArrayList(); - private ArrayList namedEvents = new ArrayList(); - - @Override - public final void preStart() { - Method[] methods = getClass().getDeclaredMethods(); - for (Method m : methods) { - if (m.getName().startsWith("onReceive") && m.getParameterTypes().length == 1) { - if (m.getName().equals("onReceive") && m.getParameterTypes()[0] == Object.class) { - continue; - } - events.add(new Event(m.getParameterTypes()[0], m)); - continue; - } - if (m.getName().startsWith("on") && m.getName().endsWith("Receive")) { - String methodName = m.getName(); - String name = methodName.substring("on".length(), methodName.length() - "Receive".length()); - if (name.length() > 0) { - name = name.substring(0, 1).toLowerCase() + name.substring(1); - namedEvents.add(new NamedEvent(name, m.getParameterTypes()[0], m)); - continue; - } - - } - } - preStartImpl(); - } - - /** - * Replacement for preStart - */ - public void preStartImpl() { - - } - - @Override - public void onReceive(Object message) { - if (message instanceof NamedMessage) { - NamedMessage named = (NamedMessage) message; - for (NamedEvent event : namedEvents) { - if (event.name.equals(named.getName())) { - if (event.check(named.getMessage())) { - try { - event.method.invoke(this, named.getMessage()); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - } - } - } - - for (Event event : events) { - if (event.check(message)) { - try { - event.method.invoke(this, message); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - return; - } - } - } - - class NamedEvent { - private String name; - private Class arg; - private Method method; - - NamedEvent(String name, Class arg, Method method) { - this.name = name; - this.arg = arg; - this.method = method; - } - - public String getName() { - return name; - } - - public Class getArg() { - return arg; - } - - public Method getMethod() { - return method; - } - - public boolean check(Object obj) { - if (arg.isAssignableFrom(obj.getClass())) { - return true; - } - return false; - } - } - - class Event { - private Class arg; - private Method method; - - Event(Class arg, Method method) { - this.arg = arg; - this.method = method; - } - - public boolean check(Object obj) { - if (arg.isAssignableFrom(obj.getClass())) { - return true; - } - return false; - } - } -} diff --git a/src/com/droidkit/actors/dispatch/AbstractDispatchQueue.java b/src/com/droidkit/actors/dispatch/AbstractDispatchQueue.java deleted file mode 100644 index 5ae209c..0000000 --- a/src/com/droidkit/actors/dispatch/AbstractDispatchQueue.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.droidkit.actors.dispatch; - -/** - * Queue for dispatching messages for {@link ThreadPoolDispatcher}. - * Implementation MUST BE thread-safe. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public abstract class AbstractDispatchQueue { - - /** - * Value used for result of waitDelay when dispatcher need to wait forever - */ - protected static final long FOREVER = Long.MAX_VALUE; - - private QueueListener listener; - - /** - * Fetch message for dispatching and removing it from dispatch queue - * - * @param time current time from ActorTime - * @return message or null if there is no message for processing - */ - public abstract T dispatch(long time); - - /** - * Expected delay for nearest message. - * You might provide most accurate value as you can, - * this will minimize unnecessary thread work. - * For example, if you will return zero here then thread will - * loop continuously and consume processor time. - * - * @param time current time from ActorTime - * @return delay in ms - */ - public abstract long waitDelay(long time); - - /** - * Implementation of adding message to queue - * - * @param message - * @param atTime - */ - protected abstract void putToQueueImpl(T message, long atTime); - - /** - * Adding message to queue - * - * @param message message - * @param atTime time (use {@link com.droidkit.actors.ActorTime#currentTime()} for currentTime) - */ - public final void putToQueue(T message, long atTime) { - putToQueueImpl(message, atTime); - notifyQueueChanged(); - } - - /** - * Notification about queue change. - */ - protected void notifyQueueChanged() { - QueueListener lListener = listener; - if (lListener != null) { - lListener.onQueueChanged(); - } - } - - /** - * Getting of current queue listener - * - * @return queue listener - */ - public QueueListener getListener() { - return listener; - } - - /** - * Setting queue listener - * - * @param listener queue listener - */ - public void setListener(QueueListener listener) { - this.listener = listener; - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/dispatch/AbstractDispatcher.java b/src/com/droidkit/actors/dispatch/AbstractDispatcher.java deleted file mode 100644 index ae0da79..0000000 --- a/src/com/droidkit/actors/dispatch/AbstractDispatcher.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.droidkit.actors.dispatch; - -/** - * Abstract thread dispatcher for messages - */ -public abstract class AbstractDispatcher> { - final private Q queue; - final Dispatch dispatch; - - protected AbstractDispatcher(Q queue, Dispatch dispatch) { - this.queue = queue; - this.dispatch = dispatch; - this.queue.setListener(new QueueListener() { - @Override - public void onQueueChanged() { - notifyDispatcher(); - } - }); - } - - /** - * Queue used for dispatching - * - * @return queue - */ - public Q getQueue() { - return queue; - } - - /** - * Actual execution of action - * - * @param message action - */ - protected void dispatchMessage(T message) { - if (dispatch != null) { - dispatch.dispatchMessage(message); - } - } - - /** - * Notification about queue change - */ - protected void notifyDispatcher() { - - } -} diff --git a/src/com/droidkit/actors/dispatch/Dispatch.java b/src/com/droidkit/actors/dispatch/Dispatch.java deleted file mode 100644 index 845b2e7..0000000 --- a/src/com/droidkit/actors/dispatch/Dispatch.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.droidkit.actors.dispatch; - -/** - * Used as callback for message processing - */ -public interface Dispatch { - void dispatchMessage(T message); -} diff --git a/src/com/droidkit/actors/dispatch/QueueListener.java b/src/com/droidkit/actors/dispatch/QueueListener.java deleted file mode 100644 index b8b6ef8..0000000 --- a/src/com/droidkit/actors/dispatch/QueueListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.droidkit.actors.dispatch; - -/** - * Listener for monitoring queue changes in dispatchers - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public interface QueueListener { - public void onQueueChanged(); -} \ No newline at end of file diff --git a/src/com/droidkit/actors/dispatch/RunnableDispatcher.java b/src/com/droidkit/actors/dispatch/RunnableDispatcher.java deleted file mode 100644 index b034562..0000000 --- a/src/com/droidkit/actors/dispatch/RunnableDispatcher.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.droidkit.actors.dispatch; - -import static com.droidkit.actors.ActorTime.currentTime; - -/** - * RunnableDispatcher is used for executing various Runnable in background - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class RunnableDispatcher extends ThreadPoolDispatcher> { - - /** - * Creating of dispatcher with one thread - */ - public RunnableDispatcher() { - this(1); - } - - /** - * Creating of dispatcher with {@code threadsCount} threads - * - * @param threadsCount number of threads - */ - public RunnableDispatcher(int threadsCount) { - super(threadsCount, new SimpleDispatchQueue()); - } - - /** - * Creating of dispatcher with {@code threadsCount} threads and {@code priority} - * - * @param threadsCount number of threads - * @param priority priority of threads - */ - public RunnableDispatcher(int threadsCount, int priority) { - super(threadsCount, priority, new SimpleDispatchQueue()); - } - - @Override - protected void dispatchMessage(Runnable object) { - object.run(); - } - - /** - * Post action to queue - * - * @param action action - */ - public void postAction(Runnable action) { - postAction(action, 0); - } - - /** - * Post action to queue with delay - * - * @param action action - * @param delay delay - */ - public void postAction(Runnable action, long delay) { - getQueue().putToQueue(action, currentTime() + delay); - } - - /** - * Removing action from queue - * - * @param action action - */ - public void removeAction(Runnable action) { - getQueue().removeFromQueue(action); - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/dispatch/SimpleDispatchQueue.java b/src/com/droidkit/actors/dispatch/SimpleDispatchQueue.java deleted file mode 100644 index 2cf4918..0000000 --- a/src/com/droidkit/actors/dispatch/SimpleDispatchQueue.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.droidkit.actors.dispatch; - -import java.util.ArrayList; -import java.util.Map; -import java.util.TreeMap; - -/** - * Simple queue implementation for dispatchers - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class SimpleDispatchQueue extends AbstractDispatchQueue { - - protected final TreeMap messages = new TreeMap(); - - protected final ArrayList freeMessages = new ArrayList(); - - /** - * Removing message from queue - * - * @param t message - */ - public void removeFromQueue(T t) { - synchronized (messages) { - for (Map.Entry messageEntry : messages.entrySet()) { - if (messageEntry.getValue().equals(t)) { - Message message = messages.remove(messageEntry.getKey()); - recycle(message); - notifyQueueChanged(); - return; - } - } - } - } - - @Override - public T dispatch(long time) { - synchronized (messages) { - if (messages.size() > 0) { - long firstKey = messages.firstKey(); - if (firstKey < time) { - Message message = messages.remove(firstKey); - T res = message.action; - recycle(message); - return res; - } - } - } - return null; - } - - @Override - public long waitDelay(long time) { - synchronized (messages) { - if (messages.size() > 0) { - long firstKey = messages.firstKey(); - if (firstKey < time) { - return 0; - } else { - return time - firstKey; - } - } - } - return FOREVER; - } - - @Override - public void putToQueueImpl(T action, long atTime) { - Message message = obtainMessage(); - message.setMessage(action, atTime); - synchronized (messages) { - while (messages.containsKey(atTime)) { - atTime++; - } - messages.put(atTime, message); - } - } - - /** - * Getting new message object for writing to queue - * - * @return Message object - */ - protected Message obtainMessage() { - synchronized (freeMessages) { - if (freeMessages.size() > 0) { - return freeMessages.remove(0); - } - } - return new Message(); - } - - /** - * Saving message object to free cache - * - * @param message Message object - */ - protected void recycle(Message message) { - synchronized (freeMessages) { - freeMessages.add(message); - } - } - - /** - * Holder for messages - */ - protected class Message { - public long destTime; - public T action; - - public void setMessage(T action, long destTime) { - this.action = action; - this.destTime = destTime; - } - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/dispatch/ThreadPoolDispatcher.java b/src/com/droidkit/actors/dispatch/ThreadPoolDispatcher.java deleted file mode 100644 index b78de1d..0000000 --- a/src/com/droidkit/actors/dispatch/ThreadPoolDispatcher.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.droidkit.actors.dispatch; - -import static com.droidkit.actors.ActorTime.currentTime; - -/** - * ThreadPoolDispatcher is used for dispatching messages on it's own threads. - * Class is completely thread-safe. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ThreadPoolDispatcher> extends AbstractDispatcher { - - private final Thread[] threads; - - private boolean isClosed = false; - - /** - * Dispatcher constructor. Create threads with NORM_PRIORITY. - * - * @param count thread count - * @param queue queue for messages - * (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information) - * @param dispatch Dispatch for message processing - */ - public ThreadPoolDispatcher(int count, Q queue, Dispatch dispatch) { - this(count, Thread.NORM_PRIORITY, queue, dispatch); - } - - /** - * Dispatcher constructor. Create threads with NORM_PRIORITY. - * Should override dispatchMessage for message processing. - * - * @param count thread count - * @param queue queue for messages - * (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information) - */ - public ThreadPoolDispatcher(int count, Q queue) { - this(count, Thread.NORM_PRIORITY, queue, null); - } - - /** - * Dispatcher constructor. Create threads with NORM_PRIORITY. - * Should override dispatchMessage for message processing. - * - * @param count thread count - * @param priority thread priority - * @param queue queue for messages - * (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information) - */ - public ThreadPoolDispatcher(int count, int priority, Q queue) { - this(count, priority, queue, null); - } - - - /** - * Dispatcher constructor - * - * @param count thread count - * @param priority thread priority - * @param queue queue for messages - * (see {@link com.droidkit.actors.dispatch.AbstractDispatchQueue} for more information) - * @param dispatch Dispatch for message processing - */ - public ThreadPoolDispatcher(int count, int priority, final Q queue, Dispatch dispatch) { - super(queue, dispatch); - - this.threads = new Thread[count]; - for (int i = 0; i < count; i++) { - this.threads[i] = new DispatcherThread(); - this.threads[i].setPriority(priority); - this.threads[i].start(); - } - } - - /** - * Closing of dispatcher no one actions will be executed after calling this method. - */ - public void close() { - isClosed = true; - notifyDispatcher(); - } - - /** - * Notification about queue change - */ - @Override - protected void notifyDispatcher() { - if (threads != null) { - synchronized (threads) { - threads.notifyAll(); - } - } - } - - /** - * Thread class for dispatching - */ - private class DispatcherThread extends Thread { - @Override - public void run() { - while (!isClosed) { - T action = getQueue().dispatch(currentTime()); - if (action == null) { - synchronized (threads) { - try { - long delay = getQueue().waitDelay(currentTime()); - if (delay > 0) { - threads.wait(delay); - } - continue; - } catch (InterruptedException e) { - e.printStackTrace(); - return; - } - } - } - - try { - dispatchMessage(action); - } catch (Throwable t) { - // Possibly danger situation, but i hope this will not corrupt JVM - // For example: on Android we could always continue execution after OutOfMemoryError - // Anyway, better to catch all errors manually in dispatchMessage - t.printStackTrace(); - } - } - } - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/mailbox/AbsActorDispatcher.java b/src/com/droidkit/actors/mailbox/AbsActorDispatcher.java deleted file mode 100644 index 951dd91..0000000 --- a/src/com/droidkit/actors/mailbox/AbsActorDispatcher.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.droidkit.actors.mailbox; - -import com.droidkit.actors.*; -import com.droidkit.actors.dispatch.AbstractDispatcher; -import com.droidkit.actors.messages.DeadLetter; -import com.droidkit.actors.messages.PoisonPill; -import com.droidkit.actors.messages.StartActor; - -import java.util.HashMap; -import java.util.UUID; - -/** - * Abstract Actor Dispatcher, used for dispatching messages for actors - */ -public abstract class AbsActorDispatcher { - - private final HashMap mailboxes = new HashMap(); - private final HashMap scopes = new HashMap(); - private final HashMap actorProps = new HashMap(); - - private final ActorSystem actorSystem; - private AbstractDispatcher dispatcher; - - public AbsActorDispatcher(ActorSystem actorSystem) { - this.actorSystem = actorSystem; - } - - protected void initDispatcher(AbstractDispatcher dispatcher) { - if (this.dispatcher != null) { - throw new RuntimeException("Double dispatcher init"); - } - this.dispatcher = dispatcher; - } - - public final ActorScope createScope(String path, Props props) { - // TODO: add path check - - Mailbox mailbox = new Mailbox(dispatcher.getQueue()); - UUID uuid = UUID.randomUUID(); - ActorRef ref = new ActorRef(actorSystem, this, uuid, path); - ActorScope scope = new ActorScope(actorSystem, mailbox, ref, this, UUID.randomUUID(), path, props); - - synchronized (mailboxes) { - mailboxes.put(mailbox, scope); - scopes.put(scope.getPath(), scope); - actorProps.put(path, props); - } - - // Sending init message - scope.getActorRef().send(StartActor.INSTANCE); - return scope; - } - - public final void disconnectScope(ActorScope scope) { - synchronized (mailboxes) { - mailboxes.remove(scope.getMailbox()); - scopes.remove(scope.getPath()); - } - for (Envelope envelope : scope.getMailbox().allEnvelopes()) { - if (envelope.getSender() != null) { - envelope.getSender().send(new DeadLetter(envelope.getMessage())); - } - } - } - - public final void sendMessage(String path, Object message, long time, ActorRef sender) { - synchronized (mailboxes) { - if (!scopes.containsKey(path)) { - if (sender != null) { - sender.send(new DeadLetter(message)); - } - } else { - Mailbox mailbox = scopes.get(path).getMailbox(); - mailbox.schedule(new Envelope(message, mailbox, sender), time); - } - } - } - - public final void sendMessageOnce(String path, Object message, long time, ActorRef sender) { - synchronized (mailboxes) { - if (!scopes.containsKey(path)) { - if (sender != null) { - sender.send(new DeadLetter(message)); - } - } else { - Mailbox mailbox = scopes.get(path).getMailbox(); - mailbox.scheduleOnce(new Envelope(message, mailbox, sender), time); - } - } - } - - - /** - * Processing of envelope - * - * @param envelope envelope - */ - protected void processEnvelope(Envelope envelope) { - ActorScope actor = null; - synchronized (mailboxes) { - actor = mailboxes.get(envelope.getMailbox()); - } - if (actor == null) { - //TODO: add logging - return; - } - - try { - if (envelope.getMessage() == StartActor.INSTANCE) { - try { - actor.createActor(); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (envelope.getMessage() == PoisonPill.INSTANCE) { - try { - actor.shutdownActor(); - } catch (Exception e) { - e.printStackTrace(); - } - } else { - CurrentActor.setCurrentActor(actor.getActor()); - actor.setSender(envelope.getSender()); - actor.getActor().onReceiveGlobal(envelope.getMessage()); - } - } finally { - dispatcher.getQueue().unlockMailbox(envelope.getMailbox()); - CurrentActor.setCurrentActor(null); - } - } -} diff --git a/src/com/droidkit/actors/mailbox/ActorDispatcher.java b/src/com/droidkit/actors/mailbox/ActorDispatcher.java deleted file mode 100644 index c6d168e..0000000 --- a/src/com/droidkit/actors/mailbox/ActorDispatcher.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.droidkit.actors.mailbox; - -import com.droidkit.actors.ActorSystem; -import com.droidkit.actors.dispatch.Dispatch; -import com.droidkit.actors.dispatch.ThreadPoolDispatcher; - -/** - * Basic ActorDispatcher backed by ThreadPoolDispatcher - */ -public class ActorDispatcher extends AbsActorDispatcher { - public ActorDispatcher(ActorSystem actorSystem, int threadsCount) { - this(actorSystem, threadsCount, Thread.MIN_PRIORITY); - } - - public ActorDispatcher(ActorSystem actorSystem, int threadsCount, int priority) { - super(actorSystem); - initDispatcher(new ThreadPoolDispatcher(threadsCount, priority, new MailboxesQueue(), - new Dispatch() { - @Override - public void dispatchMessage(Envelope message) { - processEnvelope(message); - } - })); - } -} diff --git a/src/com/droidkit/actors/mailbox/Envelope.java b/src/com/droidkit/actors/mailbox/Envelope.java deleted file mode 100644 index cfe28ca..0000000 --- a/src/com/droidkit/actors/mailbox/Envelope.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.droidkit.actors.mailbox; - -import com.droidkit.actors.ActorRef; - -/** - * Actor system envelope - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class Envelope { - private final Object message; - private final ActorRef sender; - private final Mailbox mailbox; - - /** - * Creating of envelope - * - * @param message message - * @param mailbox mailbox - * @param sender sender reference - */ - public Envelope(Object message, Mailbox mailbox, ActorRef sender) { - this.message = message; - this.sender = sender; - this.mailbox = mailbox; - } - - /** - * Message in envelope - * - * @return message - */ - public Object getMessage() { - return message; - } - - /** - * Mailbox for envelope - * - * @return mailbox - */ - public Mailbox getMailbox() { - return mailbox; - } - - /** - * Sender of message - * - * @return sender reference - */ - public ActorRef getSender() { - return sender; - } -} diff --git a/src/com/droidkit/actors/mailbox/Mailbox.java b/src/com/droidkit/actors/mailbox/Mailbox.java deleted file mode 100644 index 8202462..0000000 --- a/src/com/droidkit/actors/mailbox/Mailbox.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.droidkit.actors.mailbox; - -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Actor mailbox, queue of envelopes. - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class Mailbox { - private final ConcurrentHashMap envelopes = new ConcurrentHashMap(); - - private MailboxesQueue queue; - - /** - * Creating mailbox - * - * @param queue MailboxesQueue - */ - public Mailbox(MailboxesQueue queue) { - this.queue = queue; - } - - /** - * Send envelope at time - * - * @param envelope envelope - * @param time time - */ - public void schedule(Envelope envelope, long time) { - if (envelope.getMailbox() != this) { - throw new RuntimeException("envelope.mailbox != this mailbox"); - } - - long id = queue.sendEnvelope(envelope, time); - - envelopes.put(id, envelope); - } - - /** - * Send envelope once at time - * - * @param envelope envelope - * @param time time - */ - public void scheduleOnce(Envelope envelope, long time) { - if (envelope.getMailbox() != this) { - throw new RuntimeException("envelope.mailbox != this mailbox"); - } - - Iterator> iterator = envelopes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (isEqualEnvelope(entry.getValue(), envelope)) { - queue.removeEnvelope(entry.getKey()); - iterator.remove(); - } - } - - schedule(envelope, time); - } - - void removeEnvelope(long key) { - envelopes.remove(key); - } - - /** - * Override this if you need to change filtering for scheduleOnce behaviour. - * By default it check equality only of class names. - * - * @param a - * @param b - * @return is equal - */ - protected boolean isEqualEnvelope(Envelope a, Envelope b) { - return a.getMessage().getClass() == b.getMessage().getClass(); - } - - public synchronized Envelope[] allEnvelopes() { - return envelopes.values().toArray(new Envelope[0]); - } - - public synchronized int getMailboxSize() { - return envelopes.size(); - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/mailbox/MailboxesQueue.java b/src/com/droidkit/actors/mailbox/MailboxesQueue.java deleted file mode 100644 index e6adbbf..0000000 --- a/src/com/droidkit/actors/mailbox/MailboxesQueue.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.droidkit.actors.mailbox; - -import com.droidkit.actors.dispatch.AbstractDispatchQueue; - -import java.util.HashSet; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Queue of multiple mailboxes for MailboxesDispatcher - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class MailboxesQueue extends AbstractDispatchQueue { - - private static final long MULTIPLE = 10000L; - - private final TreeMap timeShift = new TreeMap(); - private final TreeMap envelopes = new TreeMap(); - private final HashSet blocked = new HashSet(); - - /** - * Locking mailbox from processing messages from it - * - * @param mailbox mailbox for locking - */ - public void lockMailbox(Mailbox mailbox) { - synchronized (blocked) { - blocked.add(mailbox); - } - notifyQueueChanged(); - } - - /** - * Unlocking mailbox - * - * @param mailbox mailbox for unlocking - */ - public void unlockMailbox(Mailbox mailbox) { - synchronized (blocked) { - blocked.remove(mailbox); - } - notifyQueueChanged(); - } - - /** - * Sending envelope - * - * @param envelope envelope - * @param time time (see {@link com.droidkit.actors.ActorTime#currentTime()}}) - * @return envelope real time - */ - public long sendEnvelope(Envelope envelope, long time) { - long shift = 0; - synchronized (envelopes) { - if (timeShift.containsKey(time)) { - shift = timeShift.get(time); - } - while (envelopes.containsKey(time * MULTIPLE + shift)) { - shift++; - } - - if (shift != 0) { - timeShift.put(time, shift); - } - envelopes.put(time * MULTIPLE + shift, envelope); - } - notifyQueueChanged(); - return time * MULTIPLE + shift; - } - - /** - * Removing envelope from queue - * - * @param id envelope id - */ - public void removeEnvelope(long id) { - synchronized (envelopes) { - envelopes.remove(id); - } - notifyQueueChanged(); - } - - /** - * getting first available envelope - * MUST BE wrapped with envelopes and blocked sync - * - * @return envelope entry - */ - private Map.Entry firstEnvelope() { - for (Map.Entry entry : envelopes.entrySet()) { - if (!blocked.contains(entry.getValue().getMailbox())) { - return entry; - } - } - return null; - } - - @Override - public Envelope dispatch(long time) { - time = time * MULTIPLE; - synchronized (envelopes) { - synchronized (blocked) { - Map.Entry envelope = firstEnvelope(); - if (envelope != null) { - if (envelope.getKey() < time) { - envelopes.remove(envelope.getKey()); - envelope.getValue().getMailbox().removeEnvelope(envelope.getKey()); - //TODO: Better design - // Locking of mailbox before dispatch return - blocked.add(envelope.getValue().getMailbox()); - return envelope.getValue(); - } - } - } - } - return null; - } - - @Override - public long waitDelay(long time) { - time = time * MULTIPLE; - synchronized (envelopes) { - synchronized (blocked) { - Map.Entry envelope = firstEnvelope(); - - if (envelope != null) { - if (envelope.getKey() <= time) { - return 0; - } else { - return (time - envelope.getKey()) / MULTIPLE; - } - } - } - } - return FOREVER; - } - - @Override - protected void putToQueueImpl(Envelope message, long atTime) { - sendEnvelope(message, atTime); - } -} diff --git a/src/com/droidkit/actors/messages/DeadLetter.java b/src/com/droidkit/actors/messages/DeadLetter.java deleted file mode 100644 index 4d90dfe..0000000 --- a/src/com/droidkit/actors/messages/DeadLetter.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.droidkit.actors.messages; - -/** - * DeadLetter sent whet message was not processed by target actor - */ -public class DeadLetter { - private Object message; - - public DeadLetter(Object message) { - this.message = message; - } - - public Object getMessage() { - return message; - } - - @Override - public String toString() { - return "DeadLetter(" + message + ")"; - } -} diff --git a/src/com/droidkit/actors/messages/NamedMessage.java b/src/com/droidkit/actors/messages/NamedMessage.java deleted file mode 100644 index bd1ec93..0000000 --- a/src/com/droidkit/actors/messages/NamedMessage.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.droidkit.actors.messages; - -/** - * Simple named message - */ -public class NamedMessage { - private String name; - private Object message; - - public NamedMessage(String name, Object message) { - this.name = name; - this.message = message; - } - - public String getName() { - return name; - } - - public Object getMessage() { - return message; - } -} diff --git a/src/com/droidkit/actors/messages/PoisonPill.java b/src/com/droidkit/actors/messages/PoisonPill.java deleted file mode 100644 index 8fc0f58..0000000 --- a/src/com/droidkit/actors/messages/PoisonPill.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.droidkit.actors.messages; - -/** - * PoisonPill message for killing actors - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public final class PoisonPill { - public static final PoisonPill INSTANCE = new PoisonPill(); - - private PoisonPill() { - } -} diff --git a/src/com/droidkit/actors/messages/StartActor.java b/src/com/droidkit/actors/messages/StartActor.java deleted file mode 100644 index 7b657da..0000000 --- a/src/com/droidkit/actors/messages/StartActor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.droidkit.actors.messages; - -/** - * Message for starting actors - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public final class StartActor { - public static final StartActor INSTANCE = new StartActor(); - - private StartActor() { - } -} diff --git a/src/com/droidkit/actors/tasks/ActorAskImpl.java b/src/com/droidkit/actors/tasks/ActorAskImpl.java deleted file mode 100644 index cd70da4..0000000 --- a/src/com/droidkit/actors/tasks/ActorAskImpl.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.droidkit.actors.tasks; - -import com.droidkit.actors.ActorRef; -import com.droidkit.actors.messages.DeadLetter; -import com.droidkit.actors.tasks.messages.*; - -import java.util.HashMap; - -/** - * Implementation of Ask pattern - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class ActorAskImpl { - - private HashMap asks = new HashMap(); - private int nextReqId = 1; - private ActorRef self; - - public ActorAskImpl(ActorRef self) { - this.self = self; - } - - public AskFuture combine(AskFuture... futures) { - final AskFuture resultFuture = new AskFuture(this, 0); - final CombineContainer container = new CombineContainer(futures.length); - for (int i = 0; i < futures.length; i++) { - final int index = i; - container.futures[index] = futures[index]; - container.callbacks[index] = new AskCallback() { - @Override - public void onResult(Object result) { - container.completed[index] = true; - container.results[index] = result; - boolean isCompleted = true; - for (boolean c : container.completed) { - if (!c) { - isCompleted = false; - break; - } - } - - if (isCompleted && !container.isCompleted) { - container.isCompleted = true; - for (int i = 0; i < container.futures.length; i++) { - container.futures[i].removeListener(container.callbacks[i]); - } - resultFuture.onResult(container.results); - } - } - - @Override - public void onError(Throwable throwable) { - if (!container.isCompleted) { - container.isCompleted = true; - for (int i = 0; i < container.futures.length; i++) { - container.futures[i].removeListener(container.callbacks[i]); - container.futures[i].cancel(); - } - resultFuture.onError(throwable); - } - - } - }; - container.futures[index].addListener(container.callbacks[index]); - } - return resultFuture; - } - - public AskFuture ask(ActorRef ref, long timeout, AskCallback callback) { - int reqId = nextReqId++; - AskFuture future = new AskFuture(this, reqId); - if (callback != null) { - future.addListener(callback); - } - AskContainer container = new AskContainer(future, ref, reqId); - asks.put(reqId, container); - ref.send(new TaskRequest(reqId), self); - if (timeout > 0) { - self.send(new TaskTimeout(reqId), timeout); - } - return future; - } - - public boolean onTaskResult(TaskResult result) { - AskContainer container = asks.remove(result.getRequestId()); - if (container != null) { - container.future.onResult(result.getRes()); - return true; - } - - return false; - } - - public boolean onTaskError(TaskError error) { - AskContainer container = asks.remove(error.getRequestId()); - if (container != null) { - container.future.onError(error.getThrowable()); - return true; - } - - return false; - } - - public boolean onTaskTimeout(TaskTimeout taskTimeout) { - AskContainer container = asks.remove(taskTimeout.getRequestId()); - if (container != null) { - container.future.onTimeout(); - return true; - } - - return false; - } - - public boolean onTaskCancelled(int reqId) { - AskContainer container = asks.remove(reqId); - if (container != null) { - container.ref.send(new TaskCancel(reqId), self); - return true; - } - - return false; - } - - public boolean onDeadLetter(DeadLetter letter) { - if (letter.getMessage() instanceof TaskRequest) { - TaskRequest request = (TaskRequest) letter.getMessage(); - AskContainer container = asks.remove(request.getRequestId()); - if (container != null) { - // Mimic dead letter with timeout exception - container.future.onError(new AskTimeoutException()); - return true; - } - } - - return false; - } - - private class AskContainer { - public final AskFuture future; - public final ActorRef ref; - public final int requestId; - - private AskContainer(AskFuture future, ActorRef ref, int requestId) { - this.future = future; - this.ref = ref; - this.requestId = requestId; - } - } - - private class CombineContainer { - public boolean isCompleted = false; - public Object[] results; - public boolean[] completed; - public AskFuture[] futures; - public AskCallback[] callbacks; - - public CombineContainer(int count) { - results = new Object[count]; - completed = new boolean[count]; - callbacks = new AskCallback[count]; - futures = new AskFuture[count]; - } - } -} diff --git a/src/com/droidkit/actors/tasks/AskCallback.java b/src/com/droidkit/actors/tasks/AskCallback.java deleted file mode 100644 index 0af7bd5..0000000 --- a/src/com/droidkit/actors/tasks/AskCallback.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.droidkit.actors.tasks; - -/** - * Callback for Ask pattern - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public interface AskCallback { - public void onResult(T result); - - public void onError(Throwable throwable); -} diff --git a/src/com/droidkit/actors/tasks/AskCancelledException.java b/src/com/droidkit/actors/tasks/AskCancelledException.java deleted file mode 100644 index 3e11c6b..0000000 --- a/src/com/droidkit/actors/tasks/AskCancelledException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.droidkit.actors.tasks; - -/** - * Exception about cancelling task - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class AskCancelledException extends Exception { - -} diff --git a/src/com/droidkit/actors/tasks/AskFuture.java b/src/com/droidkit/actors/tasks/AskFuture.java deleted file mode 100644 index 6d4600f..0000000 --- a/src/com/droidkit/actors/tasks/AskFuture.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.droidkit.actors.tasks; - -import java.util.LinkedList; - -/** - * Modified future for ask pattern of Actors - * Created to work only in actor threads - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class AskFuture { - - private LinkedList callbacks = new LinkedList(); - - private ActorAskImpl askImpl; - private int reqId; - - private boolean isCompleted = false; - private boolean isCanceled = false; - private boolean isError = false; - private T result = null; - private Throwable error = null; - - AskFuture(ActorAskImpl askImpl, int reqId) { - this.askImpl = askImpl; - this.reqId = reqId; - } - - public void addListener(AskCallback callback) { - callbacks.add(callback); - } - - public void removeListener(AskCallback callback) { - callbacks.remove(callback); - } - - public boolean isCompleted() { - return isCompleted; - } - - public boolean isError() { - return isError; - } - - public boolean isCanceled() { - return isCanceled; - } - - public Throwable error() { - return error; - } - - public T result() { - return result; - } - - public void cancel() { - if (isCompleted) { - return; - } - isCompleted = true; - isError = false; - isCanceled = true; - - for (AskCallback callback : callbacks) { - callback.onError(new AskCancelledException()); - } - - askImpl.onTaskCancelled(reqId); - } - - void onError(Throwable throwable) { - if (isCompleted) { - return; - } - isCompleted = true; - isCanceled = false; - isError = true; - error = throwable; - result = null; - - for (AskCallback callback : callbacks) { - callback.onError(throwable); - } - } - - void onResult(T res) { - if (isCompleted) { - return; - } - isCompleted = true; - isCanceled = false; - isError = false; - error = null; - result = res; - - for (AskCallback callback : callbacks) { - callback.onResult(res); - } - } - - void onTimeout() { - onError(new AskTimeoutException()); - } -} diff --git a/src/com/droidkit/actors/tasks/AskTimeoutException.java b/src/com/droidkit/actors/tasks/AskTimeoutException.java deleted file mode 100644 index 6793130..0000000 --- a/src/com/droidkit/actors/tasks/AskTimeoutException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.droidkit.actors.tasks; - -/** - * Exception for Ask timeout - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class AskTimeoutException extends Exception { -} diff --git a/src/com/droidkit/actors/tasks/TaskActor.java b/src/com/droidkit/actors/tasks/TaskActor.java deleted file mode 100644 index 250636b..0000000 --- a/src/com/droidkit/actors/tasks/TaskActor.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.droidkit.actors.tasks; - -import com.droidkit.actors.Actor; -import com.droidkit.actors.ActorRef; -import com.droidkit.actors.messages.PoisonPill; -import com.droidkit.actors.tasks.messages.TaskCancel; -import com.droidkit.actors.tasks.messages.TaskError; -import com.droidkit.actors.tasks.messages.TaskRequest; -import com.droidkit.actors.tasks.messages.TaskResult; - -import java.util.HashSet; - -/** - * Actor for performing various async tasks - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public abstract class TaskActor extends Actor { - private final HashSet requests = new HashSet(); - - private T result; - private boolean isCompleted; - private boolean isCompletedSuccess; - private long dieTimeout = 300; - - /** - * Timeout for dying after task complete - * - * @param timeOut timeout in ms - */ - public void setTimeOut(long timeOut) { - dieTimeout = timeOut; - } - - @Override - public void preStart() { - startTask(); - } - - @Override - public void onReceive(Object message) { - if (message instanceof TaskRequest) { - TaskRequest request = (TaskRequest) message; - if (isCompleted) { - if (isCompletedSuccess) { - reply(result); - } - } else { - TaskListener listener = new TaskListener(request.getRequestId(), sender()); - requests.add(listener); - } - } else if (message instanceof TaskCancel) { - if (isCompleted) { - return; - } - TaskCancel cancel = (TaskCancel) message; - TaskListener listener = new TaskListener(cancel.getRequestId(), sender()); - requests.remove(listener); - if (requests.size() == 0) { - onTaskObsolete(); - context().stopSelf(); - } - } else if (message instanceof Result) { - if (!isCompleted) { - Result res = (Result) message; - isCompleted = true; - isCompletedSuccess = true; - result = (T) res.getRes(); - for (TaskListener request : requests) { - request.getSender().send(new TaskResult(request.getRequestId(), result)); - } - self().send(PoisonPill.INSTANCE, dieTimeout); - } - } else if (message instanceof Error) { - if (!isCompleted) { - isCompleted = true; - Error error = (Error) message; - for (TaskListener request : requests) { - request.getSender().send(new TaskError(request.getRequestId(), error.getError())); - } - context().stopSelf(); - } - } - } - - /** - * Starting of task execution - */ - public abstract void startTask(); - - /** - * Called before killing actor after clearing TaskListeners - */ - public void onTaskObsolete() { - - } - - /** - * Call this method in any thread after task complete - * - * @param res result of task - */ - public void complete(T res) { - self().send(new Result(res)); - } - - /** - * Call this method in any thread after task exception - * - * @param t exception - */ - public void error(Throwable t) { - self().send(new Error(t)); - } - - private static class Error { - private Throwable error; - - private Error(Throwable error) { - this.error = error; - } - - public Throwable getError() { - return error; - } - } - - private static class Result { - private Object res; - - private Result(Object res) { - this.res = res; - } - - public Object getRes() { - return res; - } - } - - private static class TaskListener { - private int requestId; - private ActorRef sender; - - private TaskListener(int requestId, ActorRef sender) { - this.requestId = requestId; - this.sender = sender; - } - - public int getRequestId() { - return requestId; - } - - public ActorRef getSender() { - return sender; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TaskListener that = (TaskListener) o; - - if (requestId != that.requestId) return false; - if (!sender.equals(that.sender)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = requestId; - result = 31 * result + sender.hashCode(); - return result; - } - } -} \ No newline at end of file diff --git a/src/com/droidkit/actors/tasks/messages/TaskCancel.java b/src/com/droidkit/actors/tasks/messages/TaskCancel.java deleted file mode 100644 index 4312e0a..0000000 --- a/src/com/droidkit/actors/tasks/messages/TaskCancel.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.droidkit.actors.tasks.messages; - -/** - * Message about task cancelling - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class TaskCancel { - private int requestId; - - public TaskCancel(int requestId) { - this.requestId = requestId; - } - - public int getRequestId() { - return requestId; - } -} diff --git a/src/com/droidkit/actors/tasks/messages/TaskError.java b/src/com/droidkit/actors/tasks/messages/TaskError.java deleted file mode 100644 index be6ec8a..0000000 --- a/src/com/droidkit/actors/tasks/messages/TaskError.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.droidkit.actors.tasks.messages; - -/** - * Message about task error - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class TaskError { - private final int requestId; - private final Throwable throwable; - - public TaskError(int requestId, Throwable throwable) { - this.requestId = requestId; - this.throwable = throwable; - } - - public int getRequestId() { - return requestId; - } - - public Throwable getThrowable() { - return throwable; - } -} diff --git a/src/com/droidkit/actors/tasks/messages/TaskRequest.java b/src/com/droidkit/actors/tasks/messages/TaskRequest.java deleted file mode 100644 index c512f66..0000000 --- a/src/com/droidkit/actors/tasks/messages/TaskRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.droidkit.actors.tasks.messages; - -/** - * Message about requesting task - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class TaskRequest { - private final int requestId; - - public TaskRequest(int requestId) { - this.requestId = requestId; - } - - public int getRequestId() { - return requestId; - } -} diff --git a/src/com/droidkit/actors/tasks/messages/TaskResult.java b/src/com/droidkit/actors/tasks/messages/TaskResult.java deleted file mode 100644 index 21d0f04..0000000 --- a/src/com/droidkit/actors/tasks/messages/TaskResult.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.droidkit.actors.tasks.messages; - -/** - * Message with task result - * - * @param type of task result - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class TaskResult { - private final int requestId; - private final T res; - - public TaskResult(int requestId, T res) { - this.requestId = requestId; - this.res = res; - } - - public T getRes() { - return res; - } - - public int getRequestId() { - return requestId; - } -} diff --git a/src/com/droidkit/actors/tasks/messages/TaskTimeout.java b/src/com/droidkit/actors/tasks/messages/TaskTimeout.java deleted file mode 100644 index b104574..0000000 --- a/src/com/droidkit/actors/tasks/messages/TaskTimeout.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.droidkit.actors.tasks.messages; - -/** - * Message about Task timeout - * - * @author Stepan Ex3NDR Korshakov (me@ex3ndr.com) - */ -public class TaskTimeout { - private final int requestId; - - public TaskTimeout(int requestId) { - this.requestId = requestId; - } - - public int getRequestId() { - return requestId; - } -} diff --git a/src/org/telegram/api/engine/ApiCallback.java b/src/org/telegram/api/engine/ApiCallback.java deleted file mode 100644 index eda227a..0000000 --- a/src/org/telegram/api/engine/ApiCallback.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.telegram.api.engine; - -import org.telegram.api.TLAbsUpdates; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 11.11.13 - * Time: 7:42 - */ -public interface ApiCallback { - public void onAuthCancelled(TelegramApi api); - - public void onUpdatesInvalidated(TelegramApi api); - - public void onUpdate(TLAbsUpdates updates); -} diff --git a/src/org/telegram/api/engine/AppInfo.java b/src/org/telegram/api/engine/AppInfo.java deleted file mode 100644 index 228cc39..0000000 --- a/src/org/telegram/api/engine/AppInfo.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.telegram.api.engine; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 10.11.13 - * Time: 2:31 - */ -public class AppInfo { - protected int apiId; - protected String deviceModel; - protected String systemVersion; - protected String appVersion; - protected String langCode; - - public AppInfo(int apiId, String deviceModel, String systemVersion, String appVersion, String langCode) { - this.apiId = apiId; - this.deviceModel = deviceModel; - this.systemVersion = systemVersion; - this.appVersion = appVersion; - this.langCode = langCode; - } - - public int getApiId() { - return apiId; - } - - public String getDeviceModel() { - return deviceModel; - } - - public String getSystemVersion() { - return systemVersion; - } - - public String getAppVersion() { - return appVersion; - } - - public String getLangCode() { - return langCode; - } -} diff --git a/src/org/telegram/api/engine/GzipRequest.java b/src/org/telegram/api/engine/GzipRequest.java deleted file mode 100644 index cd1369a..0000000 --- a/src/org/telegram/api/engine/GzipRequest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.telegram.api.engine; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLGzipObject; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.zip.GZIPOutputStream; - -import static org.telegram.tl.StreamingUtils.*; -import static org.telegram.tl.StreamingUtils.writeByteArray; - -/** - * Created by ex3ndr on 07.12.13. - */ -public class GzipRequest extends TLMethod { - - private static final int CLASS_ID = TLGzipObject.CLASS_ID; - - private TLMethod method; - - public GzipRequest(TLMethod method) { - this.method = method; - } - - @Override - public T deserializeResponse(InputStream stream, TLContext context) throws IOException { - return method.deserializeResponse(stream, context); - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - ByteArrayOutputStream resOutput = new ByteArrayOutputStream(); - GZIPOutputStream gzipOutputStream = new GZIPOutputStream(resOutput); - method.serialize(gzipOutputStream); - gzipOutputStream.flush(); - gzipOutputStream.close(); - byte[] body = resOutput.toByteArray(); - writeTLBytes(body, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - throw new IOException("Unsupported operation"); - } - - @Override - public String toString() { - return "gzip<" + method + ">"; - } -} diff --git a/src/org/telegram/api/engine/Logger.java b/src/org/telegram/api/engine/Logger.java deleted file mode 100644 index ca56ccd..0000000 --- a/src/org/telegram/api/engine/Logger.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.telegram.api.engine; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 11.11.13 - * Time: 4:48 - */ -public class Logger { - private static LoggerInterface logInterface; - - public static void registerInterface(LoggerInterface logInterface) { - Logger.logInterface = logInterface; - } - - public static void w(String tag, String message) { - if (logInterface != null) { - logInterface.w(tag, message); - } else { - System.out.println(tag + ":" + message); - } - } - - public static void d(String tag, String message) { - if (logInterface != null) { - logInterface.d(tag, message); - } else { - System.out.println(tag + ":" + message); - } - } - - public static void e(String tag, Throwable t) { - if (logInterface != null) { - logInterface.e(tag, t); - } else { - t.printStackTrace(); - } - } -} diff --git a/src/org/telegram/api/engine/LoggerInterface.java b/src/org/telegram/api/engine/LoggerInterface.java deleted file mode 100644 index 6e36d1c..0000000 --- a/src/org/telegram/api/engine/LoggerInterface.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.telegram.api.engine; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 11.11.13 - * Time: 4:48 - */ -public interface LoggerInterface { - void w(String tag, String message); - - void d(String tag, String message); - - void e(String tag, Throwable t); -} diff --git a/src/org/telegram/api/engine/RpcCallback.java b/src/org/telegram/api/engine/RpcCallback.java deleted file mode 100644 index db4298c..0000000 --- a/src/org/telegram/api/engine/RpcCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.telegram.api.engine; - -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 05.11.13 - * Time: 14:10 - */ -public interface RpcCallback { - public void onResult(T result); - - public void onError(int errorCode, String message); -} diff --git a/src/org/telegram/api/engine/RpcCallbackEx.java b/src/org/telegram/api/engine/RpcCallbackEx.java deleted file mode 100644 index 565ba47..0000000 --- a/src/org/telegram/api/engine/RpcCallbackEx.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.telegram.api.engine; - -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 09.11.13 - * Time: 18:06 - */ -public interface RpcCallbackEx extends RpcCallback { - public void onConfirmed(); -} diff --git a/src/org/telegram/api/engine/RpcException.java b/src/org/telegram/api/engine/RpcException.java deleted file mode 100644 index 6fb04f5..0000000 --- a/src/org/telegram/api/engine/RpcException.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.telegram.api.engine; - -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 05.11.13 - * Time: 13:59 - */ -public class RpcException extends IOException { - private static final Pattern REGEXP_PATTERN = Pattern.compile("[A-Z_0-9]+"); - - private static String getErrorTag(String srcMessage) { - if (srcMessage == null) { - return "UNKNOWN"; - } - Matcher matcher = REGEXP_PATTERN.matcher(srcMessage); - if (matcher.find()) { - return matcher.group(); - } - return "UNKNOWN"; - } - - private static String getErrorMessage(String srcMessage) { - if (srcMessage == null) { - return "Unknown error"; - } - int index = srcMessage.indexOf(":"); - if (index > 0) { - return srcMessage.substring(index); - } else { - return srcMessage; - } - } - - private int errorCode; - - private String errorTag; - - public RpcException(int errorCode, String message) { - super(getErrorMessage(message)); - this.errorCode = errorCode; - this.errorTag = getErrorTag(message); - } - - public int getErrorCode() { - return errorCode; - } - - public String getErrorTag() { - return errorTag; - } -} diff --git a/src/org/telegram/api/engine/TelegramApi.java b/src/org/telegram/api/engine/TelegramApi.java deleted file mode 100644 index 59e4446..0000000 --- a/src/org/telegram/api/engine/TelegramApi.java +++ /dev/null @@ -1,1129 +0,0 @@ -package org.telegram.api.engine; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicInteger; - -import org.telegram.api.TLAbsUpdates; -import org.telegram.api.TLApiContext; -import org.telegram.api.TLConfig; -import org.telegram.api.auth.TLExportedAuthorization; -import org.telegram.api.engine.file.Downloader; -import org.telegram.api.engine.file.Uploader; -import org.telegram.api.engine.storage.AbsApiState; -import org.telegram.api.requests.TLRequestAuthExportAuthorization; -import org.telegram.api.requests.TLRequestAuthImportAuthorization; -import org.telegram.api.requests.TLRequestHelpGetConfig; -import org.telegram.api.requests.TLRequestInitConnection; -import org.telegram.api.requests.TLRequestInvokeWithLayer; -import org.telegram.api.requests.TLRequestUploadGetFile; -import org.telegram.api.requests.TLRequestUploadSaveBigFilePart; -import org.telegram.api.requests.TLRequestUploadSaveFilePart; -import org.telegram.api.upload.TLFile; -import org.telegram.mtproto.CallWrapper; -import org.telegram.mtproto.MTProto; -import org.telegram.mtproto.MTProtoCallback; -import org.telegram.mtproto.pq.Authorizer; -import org.telegram.mtproto.pq.PqAuth; -import org.telegram.mtproto.state.ConnectionInfo; -import org.telegram.mtproto.util.BytesCache; -import org.telegram.tl.TLBool; -import org.telegram.tl.TLBoolTrue; -import org.telegram.tl.TLBytes; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 04.11.13 - * Time: 21:54 - */ -public class TelegramApi { - - private static final AtomicInteger rpcCallIndex = new AtomicInteger(0); - - private static final AtomicInteger instanceIndex = new AtomicInteger(1000); - - private final String TAG; - - private final int INSTANCE_INDEX; - - private static final int CHANNELS_MAIN = 1; - private static final int CHANNELS_FS = 2; - - private static final int DEFAULT_TIMEOUT_CHECK = 15000; - private static final int DEFAULT_TIMEOUT = 15000; - private static final int FILE_TIMEOUT = 45000; - - private boolean isClosed; - - private int primaryDc; - - private MTProto mainProto; - - private final HashMap dcProtos = new HashMap(); - private final HashMap dcSync = new HashMap(); - - private ProtoCallback callback; - - private SenderThread senderThread; - - private final HashMap callbacks = new HashMap(); - private final HashMap sentRequests = new HashMap(); - - private TLApiContext apiContext; - - private TimeoutThread timeoutThread; - private final TreeMap timeoutTimes = new TreeMap(); - - private ConnectionThread dcThread; - private final TreeMap dcRequired = new TreeMap(); - - private HashSet registeredInApi = new HashSet(); - - private AbsApiState state; - private AppInfo appInfo; - - private ApiCallback apiCallback; - - private Downloader downloader; - - private Uploader uploader; - - public TelegramApi(AbsApiState state, AppInfo _appInfo, ApiCallback _apiCallback) { - this.INSTANCE_INDEX = instanceIndex.incrementAndGet(); - this.TAG = "TelegramApi#" + INSTANCE_INDEX; - - long start = System.currentTimeMillis(); - this.apiCallback = _apiCallback; - this.appInfo = _appInfo; - this.state = state; - this.primaryDc = state.getPrimaryDc(); - this.isClosed = false; - this.callback = new ProtoCallback(); - Logger.d(TAG, "Phase 0 in " + (System.currentTimeMillis() - start) + " ms"); - - start = System.currentTimeMillis(); - this.apiContext = new TLApiContext() { - private AtomicInteger integer = new AtomicInteger(0); - - @Override - public TLObject deserializeMessage(int clazzId, InputStream stream) throws IOException { - if (integer.incrementAndGet() % 10 == 9) { - Thread.yield(); - } - return super.deserializeMessage(clazzId, stream); - } - - @Override - public TLBytes allocateBytes(int size) { - return new TLBytes(BytesCache.getInstance().allocate(size), 0, size); - } - - @Override - public void releaseBytes(TLBytes unused) { - BytesCache.getInstance().put(unused.getData()); - } - }; - - Logger.d(TAG, "Phase 1 in " + (System.currentTimeMillis() - start) + " ms"); - - start = System.currentTimeMillis(); - this.timeoutThread = new TimeoutThread(); - this.timeoutThread.start(); - - this.dcThread = new ConnectionThread(); - this.dcThread.start(); - - this.senderThread = new SenderThread(); - this.senderThread.start(); - Logger.d(TAG, "Phase 2 in " + (System.currentTimeMillis() - start) + " ms"); - - start = System.currentTimeMillis(); - this.downloader = new Downloader(this); - this.uploader = new Uploader(this); - Logger.d(TAG, "Phase 3 in " + (System.currentTimeMillis() - start) + " ms"); - } - - public Downloader getDownloader() { - return downloader; - } - - public Uploader getUploader() { - return uploader; - } - - public void switchToDc(int dcId) { - if (this.mainProto != null) { - this.mainProto.close(); - } - this.mainProto = null; - this.primaryDc = dcId; - this.state.setPrimaryDc(dcId); - synchronized (dcRequired) { - dcRequired.notifyAll(); - } - } - - @Override - public String toString() { - return "api#" + INSTANCE_INDEX; - } - - private TLMethod wrapForDc(int dcId, TLMethod method) { - if (!registeredInApi.contains(dcId)) { - method = new TLRequestInitConnection(appInfo.getApiId(), appInfo.getDeviceModel(), - appInfo.getSystemVersion(), appInfo.getAppVersion(), appInfo.getLangCode(), method); - } - return new TLRequestInvokeWithLayer(23, method); - } - - public AbsApiState getState() { - return state; - } - - public TLApiContext getApiContext() { - return apiContext; - } - - protected void onMessageArrived(TLObject object) { - if (object instanceof TLAbsUpdates) { - Logger.d(TAG, "<< update " + object.toString()); - apiCallback.onUpdate((TLAbsUpdates) object); - } else { - Logger.d(TAG, "<< unknown object " + object.toString()); - } - } - - public boolean isClosed() { - return isClosed; - } - - public void close() { - if (!this.isClosed) { - apiCallback.onAuthCancelled(this); - this.isClosed = true; - if (this.timeoutThread != null) { - this.timeoutThread.interrupt(); - this.timeoutThread = null; - } - mainProto.close(); - } - } - - public void resetNetworkBackoff() { - if (mainProto != null) { - mainProto.resetNetworkBackoff(); - } - for (MTProto mtProto : dcProtos.values()) { - mtProto.resetNetworkBackoff(); - } - } - - public void resetConnectionInfo() { - mainProto.reloadConnectionInformation(); - synchronized (dcProtos) { - for (MTProto proto : dcProtos.values()) { - proto.reloadConnectionInformation(); - } - } - } - - // Basic sync and async methods - - private void doRpcCall(TLMethod method, int timeout, RpcCallback callback, int destDc) { - doRpcCall(method, timeout, callback, destDc, true); - } - - private void doRpcCall(TLMethod method, int timeout, RpcCallback callback, int destDc, - boolean authRequired) { - if (isClosed) { - if (callback != null) { - callback.onError(0, null); - } - return; - } - int localRpcId = rpcCallIndex.getAndIncrement(); - synchronized (callbacks) { - RpcCallbackWrapper wrapper = new RpcCallbackWrapper(localRpcId, method, callback); - wrapper.dcId = destDc; - wrapper.timeout = timeout; - wrapper.isAuthRequred = authRequired; - - callbacks.put(localRpcId, wrapper); - - if (callback != null) { - long timeoutTime = System.nanoTime() + timeout * 1000 * 1000L; - synchronized (timeoutTimes) { - while (timeoutTimes.containsKey(timeoutTime)) { - timeoutTime++; - } - timeoutTimes.put(timeoutTime, localRpcId); - timeoutTimes.notifyAll(); - } - wrapper.timeoutTime = timeoutTime; - } - - if (authRequired) { - checkDcAuth(destDc); - } else { - checkDc(destDc); - } - - callbacks.notifyAll(); - } - - Logger.d(TAG, ">> #" + +localRpcId + ": " + method.toString()); - } - - private T doRpcCall(TLMethod method, int timeout, int destDc) throws IOException { - return doRpcCall(method, timeout, destDc, true); - } - - private T doRpcCall(TLMethod method, int timeout, int destDc, boolean authRequired) throws IOException { - if (isClosed) { - throw new TimeoutException(); - } - final Object waitObj = new Object(); - final Object[] res = new Object[3]; - final boolean[] completed = new boolean[1]; - completed[0] = false; - - doRpcCall(method, timeout, new RpcCallback() { - @Override - public void onResult(T result) { - synchronized (waitObj) { - if (completed[0]) { - return; - } - completed[0] = true; - res[0] = result; - res[1] = null; - res[2] = null; - waitObj.notifyAll(); - } - } - - @Override - public void onError(int errorCode, String message) { - synchronized (waitObj) { - if (completed[0]) { - return; - } - completed[0] = true; - res[0] = null; - res[1] = errorCode; - res[2] = message; - waitObj.notifyAll(); - } - } - }, destDc, authRequired); - - synchronized (waitObj) { - try { - waitObj.wait(timeout); - completed[0] = true; - } catch (InterruptedException e) { - throw new TimeoutException(); - } - } - - if (res[0] == null) { - if (res[1] != null) { - Integer code = (Integer) res[1]; - if (code == 0) { - throw new TimeoutException(); - } else { - throw new RpcException(code, (String) res[2]); - } - } else { - throw new TimeoutException(); - } - } else { - return (T) res[0]; - } - } - - // Public async methods - public void doRpcCallWeak(TLMethod method) { - doRpcCallWeak(method, DEFAULT_TIMEOUT); - } - - public void doRpcCallWeak(TLMethod method, int timeout) { - doRpcCall(method, timeout, (RpcCallback) null); - } - - public void doRpcCall(TLMethod method, RpcCallback callback) { - doRpcCall(method, DEFAULT_TIMEOUT, callback); - } - - public void doRpcCall(TLMethod method, int timeout, RpcCallback callback) { - doRpcCall(method, timeout, callback, 0); - } - - // Public sync methods - - public T doRpcCall(TLMethod method) throws IOException { - return doRpcCall(method, DEFAULT_TIMEOUT); - } - - public T doRpcCall(TLMethod method, int timeout) throws IOException { - return doRpcCall(method, timeout, 0); - } - - public T doRpcCallSide(TLMethod method) throws IOException { - return doRpcCall(method, DEFAULT_TIMEOUT, primaryDc, true); - } - - public T doRpcCallSide(TLMethod method, int timeout) throws IOException { - return doRpcCall(method, timeout, primaryDc, true); - } - - public T doRpcCallSideGzip(TLMethod method, int timeout) throws IOException { - return doRpcCall(new GzipRequest(method), timeout, primaryDc, true); - } - - public T doRpcCallGzip(TLMethod method, int timeout) throws IOException { - return doRpcCall(new GzipRequest(method), timeout, 0); - } - - public T doRpcCallNonAuth(TLMethod method) throws IOException { - return doRpcCallNonAuth(method, DEFAULT_TIMEOUT, primaryDc); - } - - public T doRpcCallNonAuth(TLMethod method, int dcId) throws IOException { - return doRpcCallNonAuth(method, DEFAULT_TIMEOUT, dcId); - } - - public T doRpcCallNonAuth(TLMethod method, int timeout, int dcId) throws IOException { - return doRpcCall(method, timeout, dcId, false); - } - - public void doRpcCallNonAuth(TLMethod method, int timeout, RpcCallback callback) { - doRpcCall(method, timeout, callback, 0, false); - } - - public boolean doSaveFilePart(long _fileId, int _filePart, byte[] _bytes) throws IOException { - TLBool res = doRpcCall( - new TLRequestUploadSaveFilePart(_fileId, _filePart, _bytes), - FILE_TIMEOUT, - primaryDc, - true); - return res instanceof TLBoolTrue; - } - - public boolean doSaveBigFilePart(long _fileId, int _filePart, int _totalParts, byte[] _bytes) throws IOException { - TLBool res = doRpcCall( - new TLRequestUploadSaveBigFilePart(_fileId, _filePart, _totalParts, _bytes), - FILE_TIMEOUT, - primaryDc); - return res instanceof TLBoolTrue; - } - - public TLFile doGetFile(int dcId, org.telegram.api.TLAbsInputFileLocation _location, int _offset, int _limit) throws IOException { - return doRpcCall(new TLRequestUploadGetFile(_location, _offset, _limit), FILE_TIMEOUT, dcId); - } - - private void checkDcAuth(int dcId) { - if (dcId != 0) { - synchronized (dcProtos) { - if (!dcProtos.containsKey(dcId)) { - synchronized (dcRequired) { - dcRequired.put(dcId, true); - dcRequired.notifyAll(); - } - } else if (!state.isAuthenticated(dcId)) { - synchronized (dcRequired) { - dcRequired.put(dcId, true); - dcRequired.notifyAll(); - } - } - } - } - } - - private void checkDc(int dcId) { - if (dcId != 0) { - synchronized (dcProtos) { - if (!dcProtos.containsKey(dcId)) { - synchronized (dcRequired) { - if (!dcRequired.containsKey(dcId)) { - dcRequired.put(dcId, false); - } - dcRequired.notifyAll(); - } - } - } - } else if (mainProto == null) { - synchronized (dcRequired) { - dcRequired.notifyAll(); - } - } - } - - private class ProtoCallback implements MTProtoCallback { - - @Override - public void onSessionCreated(MTProto proto) { - if (isClosed) { - return; - } - - Logger.w(TAG, proto + ": onSessionCreated"); - - if (proto == mainProto) { - registeredInApi.add(primaryDc); - } else { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - registeredInApi.add(p.getKey()); - break; - } - } - } - - apiCallback.onUpdatesInvalidated(TelegramApi.this); - } - - @Override - public void onAuthInvalidated(MTProto proto) { - if (isClosed) { - return; - } - - if (proto == mainProto) { - synchronized (dcRequired) { - mainProto.close(); - mainProto = null; - state.setAuthenticated(primaryDc, false); - dcRequired.notifyAll(); - } - - synchronized (dcProtos) { - for (Map.Entry p : dcProtos.entrySet()) { - p.getValue().close(); - state.setAuthenticated(p.getKey(), false); - } - } - - apiCallback.onAuthCancelled(TelegramApi.this); - } else { - synchronized (dcProtos) { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - state.setAuthenticated(p.getKey(), false); - dcProtos.remove(p.getKey()); - break; - } - } - } - synchronized (dcRequired) { - dcRequired.notifyAll(); - } - } - } - - @Override - public void onApiMessage(byte[] message, MTProto proto) { - if (isClosed) { - return; - } - - if (proto == mainProto) { - registeredInApi.add(primaryDc); - } else { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - registeredInApi.add(p.getKey()); - break; - } - } - } - - try { - TLObject object = apiContext.deserializeMessage(message); - onMessageArrived(object); - } catch (Throwable t) { - Logger.e(TAG, t); - } - } - - @Override - public void onRpcResult(int callId, byte[] response, MTProto proto) { - if (isClosed) { - return; - } - - if (proto == mainProto) { - registeredInApi.add(primaryDc); - } else { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - registeredInApi.add(p.getKey()); - break; - } - } - } - - try { - RpcCallbackWrapper currentCallback = null; - synchronized (callbacks) { - if (sentRequests.containsKey(callId)) { - currentCallback = callbacks.remove(sentRequests.remove(callId)); - } - } - if (currentCallback != null && currentCallback.method != null) { - long start = System.currentTimeMillis(); - TLObject object = currentCallback.method.deserializeResponse(response, apiContext); - Logger.d(TAG, "<< #" + +currentCallback.id + " deserialized " + object + " in " + (System.currentTimeMillis() - start) + " ms"); - - synchronized (currentCallback) { - if (currentCallback.isCompleted) { - Logger.d(TAG, "<< #" + +currentCallback.id + " ignored " + object + " in " + currentCallback.elapsed() + " ms"); - return; - } else { - currentCallback.isCompleted = true; - } - } - Logger.d(TAG, "<< #" + +currentCallback.id + " " + object + " in " + currentCallback.elapsed() + " ms"); - - synchronized (timeoutTimes) { - timeoutTimes.remove(currentCallback.timeoutTime); - } - if (currentCallback.callback != null) { - currentCallback.callback.onResult(object); - } - } - } catch (Throwable t) { - Logger.e(TAG, t); - } - } - - @Override - public void onRpcError(int callId, int errorCode, String message, MTProto proto) { - if (isClosed) { - return; - } - - if (errorCode == 400 && message != null && - (message.startsWith("CONNECTION_NOT_INITED") || message.startsWith("CONNECTION_LAYER_INVALID"))) { - Logger.w(TAG, proto + ": (!)Error #400 " + message); - - int dc = -1; - if (proto == mainProto) { - dc = primaryDc; - } else { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - dc = p.getKey(); - break; - } - } - } - if (dc < 0) { - return; - } - registeredInApi.remove(dc); - - RpcCallbackWrapper currentCallback; - synchronized (callbacks) { - currentCallback = callbacks.remove(sentRequests.remove(callId)); - if (currentCallback != null) { - currentCallback.isSent = false; - callbacks.notifyAll(); - } - } - - return; - } else { - if (proto == mainProto) { - registeredInApi.add(primaryDc); - } else { - for (Map.Entry p : dcProtos.entrySet()) { - if (p.getValue() == proto) { - registeredInApi.add(p.getKey()); - break; - } - } - } - } - - try { - RpcCallbackWrapper currentCallback = null; - synchronized (callbacks) { - if (sentRequests.containsKey(callId)) { - currentCallback = callbacks.remove(sentRequests.remove(callId)); - } - } - if (currentCallback != null) { - synchronized (currentCallback) { - if (currentCallback.isCompleted) { - Logger.d(TAG, "<< #" + +currentCallback.id + " ignored error #" + errorCode + " " + message + " in " + currentCallback.elapsed() + " ms"); - return; - } else { - currentCallback.isCompleted = true; - } - } - Logger.d(TAG, "<< #" + +currentCallback.id + " error #" + errorCode + " " + message + " in " + currentCallback.elapsed() + " ms"); - synchronized (timeoutTimes) { - timeoutTimes.remove(currentCallback.timeoutTime); - } - if (currentCallback.callback != null) { - currentCallback.callback.onError(errorCode, message); - } - } - } catch (Throwable t) { - Logger.e(TAG, t); - } - } - - @Override - public void onConfirmed(int callId) { - RpcCallbackWrapper currentCallback = null; - synchronized (callbacks) { - if (sentRequests.containsKey(callId)) { - currentCallback = callbacks.get(sentRequests.get(callId)); - } - } - if (currentCallback != null) { - Logger.d(TAG, "<< #" + +currentCallback.id + " confirmed in " + currentCallback.elapsed() + " ms"); - synchronized (currentCallback) { - if (currentCallback.isCompleted || currentCallback.isConfirmed) { - return; - } else { - currentCallback.isConfirmed = true; - } - } - if (currentCallback.callback instanceof RpcCallbackEx) { - ((RpcCallbackEx) currentCallback.callback).onConfirmed(); - } - } - } - } - - private class SenderThread extends Thread { - public SenderThread() { - setName("Sender#" + hashCode()); - } - - @Override - public void run() { - setPriority(Thread.MIN_PRIORITY); - while (!isClosed) { - Logger.d(TAG, "Sender iteration"); - RpcCallbackWrapper wrapper = null; - synchronized (callbacks) { - for (RpcCallbackWrapper w : callbacks.values()) { - if (!w.isSent) { - if (w.dcId == 0 && mainProto != null) { - if (state.isAuthenticated(primaryDc) || !w.isAuthRequred) { - wrapper = w; - break; - } - } - if (w.dcId != 0 && dcProtos.containsKey(w.dcId)) { - if (state.isAuthenticated(w.dcId) || !w.isAuthRequred) { - wrapper = w; - break; - } - } - } - } - if (wrapper == null) { - try { - callbacks.wait(); - } catch (InterruptedException e) { - Logger.e(TAG, e); - return; - } - continue; - } - } - - if (mainProto == null) { - continue; - } - - if (wrapper.dcId == 0) { - if (!state.isAuthenticated(primaryDc) && wrapper.isAuthRequred) { - continue; - } - synchronized (callbacks) { - boolean isHighPriority = wrapper.callback != null && wrapper.callback instanceof RpcCallbackEx; - int rpcId = mainProto.sendRpcMessage(wrapper.method, wrapper.timeout, isHighPriority); - sentRequests.put(rpcId, wrapper.id); - wrapper.isSent = true; - Logger.d(TAG, "#> #" + wrapper.id + " sent to MTProto #" + mainProto.getInstanceIndex() + " with id #" + rpcId); - } - } else { - if (!dcProtos.containsKey(wrapper.dcId) || (!state.isAuthenticated(wrapper.dcId) && wrapper.isAuthRequred)) { - continue; - } - - MTProto proto = dcProtos.get(wrapper.dcId); - synchronized (callbacks) { - boolean isHighPriority = wrapper.callback != null && wrapper.callback instanceof RpcCallbackEx; - int rpcId = proto.sendRpcMessage(wrapper.method, wrapper.timeout, isHighPriority); - sentRequests.put(rpcId, wrapper.id); - wrapper.isSent = true; - Logger.d(TAG, "#> #" + wrapper.id + " sent to MTProto #" + proto.getInstanceIndex() + " with id #" + rpcId); - } - } - } - } - } - - private class ConnectionThread extends Thread { - public ConnectionThread() { - setName("Connection#" + hashCode()); - } - - private MTProto waitForDc(final int dcId) throws IOException { - Logger.d(TAG, "#" + dcId + ": waitForDc"); - if (isClosed) { - Logger.w(TAG, "#" + dcId + ": Api is closed"); - throw new TimeoutException(); - } - -// if (!state.isAuthenticated(primaryDc)) { -// Logger.w(TAG, "#" + dcId + ": Dc is not authenticated"); -// throw new TimeoutException(); -// } - - Object syncObj; - synchronized (dcSync) { - syncObj = dcSync.get(dcId); - if (syncObj == null) { - syncObj = new Object(); - dcSync.put(dcId, syncObj); - } - } - - synchronized (syncObj) { - MTProto proto; - synchronized (dcProtos) { - proto = dcProtos.get(dcId); - if (proto != null) { - if (proto.isClosed()) { - Logger.d(TAG, "#" + dcId + "proto removed because of death"); - dcProtos.remove(dcId); - proto = null; - } - } - } - - if (proto == null) { - Logger.d(TAG, "#" + dcId + ": Creating proto for dc"); - ConnectionInfo[] connectionInfo = state.getAvailableConnections(dcId); - - if (connectionInfo.length == 0) { - Logger.w(TAG, "#" + dcId + ": Unable to find proper dc config"); - TLConfig config = doRpcCall(new TLRequestHelpGetConfig()); - state.updateSettings(config); - resetConnectionInfo(); - connectionInfo = state.getAvailableConnections(dcId); - } - - if (connectionInfo.length == 0) { - Logger.w(TAG, "#" + dcId + ": Still unable to find proper dc config"); - throw new TimeoutException(); - } - - if (state.getAuthKey(dcId) != null) { - byte[] authKey = state.getAuthKey(dcId); - if (authKey == null) { - throw new TimeoutException(); - } - proto = new MTProto(state.getMtProtoState(dcId), callback, - new CallWrapper() { - @Override - public TLObject wrapObject(TLMethod srcRequest) { - return wrapForDc(dcId, srcRequest); - } - }, CHANNELS_FS, MTProto.MODE_GENERAL); - - dcProtos.put(dcId, proto); - return proto; - } else { - Logger.w(TAG, "#" + dcId + ": Creating key"); - Authorizer authorizer = new Authorizer(); - PqAuth auth = authorizer.doAuth(connectionInfo); - if (auth == null) { - Logger.w(TAG, "#" + dcId + ": Timed out"); - throw new TimeoutException(); - } - state.putAuthKey(dcId, auth.getAuthKey()); - state.setAuthenticated(dcId, false); - state.getMtProtoState(dcId).initialServerSalt(auth.getServerSalt()); - - byte[] authKey = state.getAuthKey(dcId); - if (authKey == null) { - Logger.w(TAG, "#" + dcId + ": auth key == null"); - throw new TimeoutException(); - } - - proto = new MTProto(state.getMtProtoState(dcId), callback, - new CallWrapper() { - @Override - public TLObject wrapObject(TLMethod srcRequest) { - return wrapForDc(dcId, srcRequest); - } - }, CHANNELS_FS, MTProto.MODE_GENERAL); - - dcProtos.put(dcId, proto); - - return proto; - } - } else { - Logger.w(TAG, "#" + dcId + ": returning proper proto"); - return proto; - } - } - } - - private MTProto waitForAuthDc(final int dcId) throws IOException { - Logger.d(TAG, "#" + dcId + ": waitForAuthDc"); - if (isClosed) { - Logger.w(TAG, "#" + dcId + ": Api is closed"); - throw new TimeoutException(); - } - - MTProto proto = waitForDc(dcId); - - if (!state.isAuthenticated(dcId)) { - Logger.w(TAG, "#" + dcId + ": exporting auth"); - TLExportedAuthorization exAuth = doRpcCall(new TLRequestAuthExportAuthorization(dcId)); - - Logger.w(TAG, "#" + dcId + ": importing auth"); - doRpcCallNonAuth(new TLRequestAuthImportAuthorization(exAuth.getId(), exAuth.getBytes()), DEFAULT_TIMEOUT, dcId); - - state.setAuthenticated(dcId, true); - } - - return proto; - } - - @Override - public void run() { - setPriority(Thread.MIN_PRIORITY); - while (!isClosed) { - Logger.d(TAG, "Connection iteration"); - if (mainProto == null) { - if (state.getAuthKey(primaryDc) == null) { - try { - long start = System.currentTimeMillis(); - waitForDc(primaryDc); - mainProto = new MTProto(state.getMtProtoState(primaryDc), callback, - new CallWrapper() { - @Override - public TLObject wrapObject(TLMethod srcRequest) { - return wrapForDc(primaryDc, srcRequest); - } - }, CHANNELS_MAIN, MTProto.MODE_GENERAL); - Logger.d(TAG, "#MTProto #" + mainProto.getInstanceIndex() + " created in " + (System.currentTimeMillis() - start) + " ms"); - } catch (IOException e) { - Logger.e(TAG, e); - try { - Thread.sleep(1000); - continue; - } catch (InterruptedException e1) { - Logger.e(TAG, e1); - return; - } - } - } else { - long start = System.currentTimeMillis(); - mainProto = new MTProto(state.getMtProtoState(primaryDc), callback, - new CallWrapper() { - @Override - public TLObject wrapObject(TLMethod srcRequest) { - return wrapForDc(primaryDc, srcRequest); - } - }, CHANNELS_MAIN, MTProto.MODE_GENERAL); - Logger.d(TAG, "#MTProto #" + mainProto.getInstanceIndex() + " created in " + (System.currentTimeMillis() - start) + " ms"); - } - synchronized (callbacks) { - callbacks.notifyAll(); - } - continue; - } - - Integer dcId = null; - Boolean authRequired = null; - synchronized (dcRequired) { - if (dcRequired.isEmpty()) { - dcId = null; - authRequired = null; - } else { - try { - dcId = dcRequired.firstKey(); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - - if (dcId == null) { - try { - dcRequired.wait(); - } catch (InterruptedException e) { - // e.printStackTrace(); - } - continue; - } - - authRequired = dcRequired.remove(dcId); - } - - if (dcProtos.containsKey(dcId)) { - if (authRequired && !state.isAuthenticated(dcId) && state.isAuthenticated(primaryDc)) { - try { - waitForAuthDc(dcId); - synchronized (callbacks) { - callbacks.notifyAll(); - } - } catch (IOException e) { - try { - Thread.sleep(1000); - continue; - } catch (InterruptedException e1) { - Logger.e(TAG, e1); - return; - } - } - } - } else { - try { - if (authRequired && !state.isAuthenticated(dcId) && state.isAuthenticated(primaryDc)) { - waitForAuthDc(dcId); - } else { - waitForDc(dcId); - } - synchronized (callbacks) { - callbacks.notifyAll(); - } - } catch (IOException e) { - Logger.e(TAG, e); - } - } - } - } - } - - private class TimeoutThread extends Thread { - public TimeoutThread() { - setName("Timeout#" + hashCode()); - } - - @Override - public void run() { - while (!isClosed) { - Logger.d(TAG, "Timeout Iteration"); - Long key = null; - Integer id = null; - synchronized (timeoutTimes) { - if (timeoutTimes.isEmpty()) { - key = null; - } else { - try { - key = timeoutTimes.firstKey(); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - - if (key == null) { - try { - timeoutTimes.wait(); - } catch (InterruptedException e) { - // e.printStackTrace(); - } - continue; - } - - long delta = (key - System.nanoTime()) / (1000 * 1000); - if (delta > 0) { - try { - timeoutTimes.wait(delta); - } catch (InterruptedException e) { - // e.printStackTrace(); - } - continue; - } - - id = timeoutTimes.remove(key); - if (id == null) { - continue; - } - } - - RpcCallbackWrapper currentCallback; - synchronized (callbacks) { - currentCallback = callbacks.remove(id); - } - if (currentCallback != null) { - synchronized (currentCallback) { - if (currentCallback.isCompleted) { - Logger.d(TAG, "RPC #" + id + ": Timeout ignored"); - return; - } else { - currentCallback.isCompleted = true; - } - } - Logger.d(TAG, "RPC #" + id + ": Timeout (" + currentCallback.elapsed() + " ms)"); - currentCallback.callback.onError(0, null); - } else { - Logger.d(TAG, "RPC #" + id + ": Timeout ignored2"); - } - } - synchronized (timeoutTimes) { - for (Map.Entry entry : timeoutTimes.entrySet()) { - RpcCallbackWrapper currentCallback; - synchronized (callbacks) { - currentCallback = callbacks.remove(entry.getValue()); - } - if (currentCallback != null) { - synchronized (currentCallback) { - if (currentCallback.isCompleted) { - return; - } else { - currentCallback.isCompleted = true; - } - } - Logger.d(TAG, "RPC #" + entry.getValue() + ": Timeout (" + currentCallback.elapsed() + " ms)"); - currentCallback.callback.onError(0, null); - } - } - } - } - } - - private class RpcCallbackWrapper { - public int id; - public long requestTime = System.currentTimeMillis(); - public boolean isSent = false; - public boolean isCompleted = false; - public boolean isConfirmed = false; - public RpcCallback callback; - public long timeoutTime; - public long timeout; - public TLMethod method; - - public boolean isAuthRequred; - public int dcId; - - private RpcCallbackWrapper(int id, TLMethod method, RpcCallback callback) { - this.id = id; - this.method = method; - this.callback = callback; - } - - public long elapsed() { - return System.currentTimeMillis() - requestTime; - } - } -} diff --git a/src/org/telegram/api/engine/TimeoutException.java b/src/org/telegram/api/engine/TimeoutException.java deleted file mode 100644 index 13acc0f..0000000 --- a/src/org/telegram/api/engine/TimeoutException.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.telegram.api.engine; - -import java.io.IOException; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 06.11.13 - * Time: 1:27 - */ -public class TimeoutException extends IOException { - public TimeoutException() { - } - - public TimeoutException(String s) { - super(s); - } - - public TimeoutException(String s, Throwable throwable) { - super(s, throwable); - } - - public TimeoutException(Throwable throwable) { - super(throwable); - } -} diff --git a/src/org/telegram/api/engine/file/DownloadListener.java b/src/org/telegram/api/engine/file/DownloadListener.java deleted file mode 100644 index b633f47..0000000 --- a/src/org/telegram/api/engine/file/DownloadListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.telegram.api.engine.file; - -/** - * Created by ex3ndr on 18.11.13. - */ -public interface DownloadListener { - public void onPartDownloaded(int percent, int downloadedSize); - - public void onDownloaded(); - - public void onFailed(); -} diff --git a/src/org/telegram/api/engine/file/Downloader.java b/src/org/telegram/api/engine/file/Downloader.java deleted file mode 100644 index ac25966..0000000 --- a/src/org/telegram/api/engine/file/Downloader.java +++ /dev/null @@ -1,368 +0,0 @@ -package org.telegram.api.engine.file; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -import org.telegram.api.TLAbsInputFileLocation; -import org.telegram.api.engine.Logger; -import org.telegram.api.engine.TelegramApi; -import org.telegram.api.upload.TLFile; - -/** - * Created by ex3ndr on 18.11.13. - */ -public class Downloader { - public static final int FILE_QUEUED = 0; - public static final int FILE_DOWNLOADING = 1; - public static final int FILE_COMPLETED = 2; - public static final int FILE_CANCELED = 3; - public static final int FILE_FAILURE = 4; - - private final AtomicInteger fileIds = new AtomicInteger(1); - - private final String TAG; - - private TelegramApi api; - - private static final long DOWNLOAD_TIMEOUT = 30 * 1000; - - private static final long DEFAULT_DELAY = 15 * 1000; - - private static final int BLOCK_SIZE = 16 * 1024; - - private static final int PARALLEL_DOWNLOAD_COUNT = 2; - - private static final int PARALLEL_PARTS_COUNT = 4; - - private static final int BLOCK_QUEUED = 0; - private static final int BLOCK_DOWNLOADING = 1; - private static final int BLOCK_COMPLETED = 2; - - private ArrayList tasks = new ArrayList(); - - private ArrayList threads = new ArrayList(); - - private final Object threadLocker = new Object(); - - private Random rnd = new Random(); - - public Downloader(TelegramApi api) { - this.TAG = api.toString() + "#Downloader"; - this.api = api; - - for (int i = 0; i < PARALLEL_PARTS_COUNT; i++) { - DownloadFileThread thread = new DownloadFileThread(); - thread.start(); - threads.add(thread); - } - } - - public TelegramApi getApi() { - return api; - } - - private synchronized DownloadTask getTask(int taskId) { - for (DownloadTask task : tasks) { - if (task.taskId == taskId) { - return task; - } - } - return null; - } - - public synchronized void cancelTask(int taskId) { - DownloadTask task = getTask(taskId); - if (task != null && task.state != FILE_COMPLETED) { - task.state = FILE_CANCELED; - Logger.d(TAG, "File #" + task.taskId + "| Canceled"); - } - updateFileQueueStates(); - } - - public synchronized int getTaskState(int taskId) { - DownloadTask task = getTask(taskId); - if (task != null) { - return task.state; - } - - return FILE_CANCELED; - } - - public void waitForTask(int taskId) { - while (true) { - int state = getTaskState(taskId); - if ((state == FILE_COMPLETED) || (state == FILE_FAILURE) || (state == FILE_CANCELED)) { - return; - } - synchronized (threadLocker) { - try { - threadLocker.wait(DEFAULT_DELAY); - } catch (InterruptedException e) { - Logger.e(TAG, e); - return; - } - } - } - } - - public synchronized int requestTask(int dcId, TLAbsInputFileLocation location, int size, String destFile, DownloadListener listener) { - int blockSize = BLOCK_SIZE; - int totalBlockCount = (int) Math.ceil(((double) size) / blockSize); - - DownloadTask task = new DownloadTask(); - task.listener = listener; - task.blockSize = blockSize; - task.destFile = destFile; - try { - task.file = new RandomAccessFile(destFile, "rw"); - task.file.setLength(size); - } catch (FileNotFoundException e) { - Logger.e(TAG, e); - } catch (IOException e) { - Logger.e(TAG, e); - } - task.taskId = fileIds.getAndIncrement(); - task.dcId = dcId; - task.location = location; - task.size = size; - task.blocks = new DownloadBlock[totalBlockCount]; - for (int i = 0; i < totalBlockCount; i++) { - task.blocks[i] = new DownloadBlock(); - task.blocks[i].task = task; - task.blocks[i].index = i; - task.blocks[i].state = BLOCK_QUEUED; - } - task.state = FILE_QUEUED; - task.queueTime = System.nanoTime(); - tasks.add(task); - - Logger.d(TAG, "File #" + task.taskId + "| Requested"); - - updateFileQueueStates(); - - return task.taskId; - } - - private synchronized DownloadTask[] getActiveTasks() { - ArrayList res = new ArrayList(); - for (DownloadTask task : tasks) { - if (task.state == FILE_DOWNLOADING) { - res.add(task); - } - } - return res.toArray(new DownloadTask[res.size()]); - } - - private synchronized void updateFileQueueStates() { - DownloadTask[] activeTasks = getActiveTasks(); - outer: - for (DownloadTask task : activeTasks) { - for (DownloadBlock block : task.blocks) { - if (block.state != BLOCK_COMPLETED) { - continue outer; - } - } - onTaskCompleted(task); - } - activeTasks = getActiveTasks(); - - int count = activeTasks.length; - while (count < PARALLEL_DOWNLOAD_COUNT) { - long mintime = Long.MAX_VALUE; - DownloadTask minTask = null; - for (DownloadTask task : tasks) { - if (task.state == FILE_QUEUED && task.queueTime < mintime) { - minTask = task; - } - } - - if (minTask == null) { - break; - } - minTask.state = FILE_DOWNLOADING; - Logger.d(TAG, "File #" + minTask.taskId + "| Downloading"); - } - - synchronized (threadLocker) { - threadLocker.notifyAll(); - } - } - - private synchronized void onTaskCompleted(DownloadTask task) { - if (task.state != FILE_COMPLETED) { - Logger.d(TAG, "File #" + task.taskId + "| Completed in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms"); - task.state = FILE_COMPLETED; - try { - if (task.file != null) { - task.file.close(); - task.file = null; - } - } catch (IOException e) { - Logger.e(TAG, e); - } - } - updateFileQueueStates(); - } - - private synchronized void onTaskFailure(DownloadTask task) { - if (task.state != FILE_FAILURE) { - Logger.d(TAG, "File #" + task.taskId + "| Failure in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms"); - task.state = FILE_FAILURE; - try { - if (task.file != null) { - task.file.close(); - task.file = null; - } - } catch (IOException e) { - Logger.e(TAG, e); - } - } - updateFileQueueStates(); - } - - private synchronized DownloadTask fetchTask() { - DownloadTask[] activeTasks = getActiveTasks(); - if (activeTasks.length == 0) { - return null; - } else if (activeTasks.length == 1) { - return activeTasks[0]; - } else { - return activeTasks[rnd.nextInt(activeTasks.length)]; - } - } - - private synchronized DownloadBlock fetchBlock() { - DownloadTask task = fetchTask(); - if (task == null) { - return null; - } - - for (int i = 0; i < task.blocks.length; i++) { - if (task.blocks[i].state == BLOCK_QUEUED) { - task.blocks[i].state = BLOCK_DOWNLOADING; - if (task.lastSuccessBlock == 0) { - task.lastSuccessBlock = System.nanoTime(); - } - return task.blocks[i]; - } - } - - return null; - } - - private synchronized void onBlockDownloaded(DownloadBlock block, byte[] data) { - try { - if (block.task.file != null) { - block.task.file.seek(block.index * block.task.blockSize); - block.task.file.write(data); - } else { - return; - } - } catch (IOException e) { - Logger.e(TAG, e); - } - block.task.lastSuccessBlock = System.nanoTime(); - block.state = BLOCK_COMPLETED; - if (block.task.listener != null) { - int downloadedCount = 0; - for (DownloadBlock b : block.task.blocks) { - if (b.state == BLOCK_COMPLETED) { - downloadedCount++; - } - } - - int percent = downloadedCount * 100 / block.task.blocks.length; - block.task.listener.onPartDownloaded(percent, downloadedCount); - } - updateFileQueueStates(); - } - - private synchronized void onBlockFailure(DownloadBlock block) { - block.state = BLOCK_QUEUED; - if (block.task.lastSuccessBlock != 0 && (System.nanoTime() - block.task.lastSuccessBlock > DOWNLOAD_TIMEOUT * 1000L * 1000L)) { - onTaskFailure(block.task); - } - updateFileQueueStates(); - } - - private class DownloadTask { - - public DownloadListener listener; - public long lastNotifyTime; - - public int taskId; - - public int blockSize; - - public int dcId; - public TLAbsInputFileLocation location; - public int size; - - public long queueTime; - - public int state; - - public DownloadBlock[] blocks; - - public String destFile; - - public RandomAccessFile file; - - public long lastSuccessBlock; - } - - private class DownloadBlock { - public DownloadTask task; - public int state; - public int index; - } - - private class DownloadFileThread extends Thread { - - public DownloadFileThread() { - setName("DownloadFileThread#" + hashCode()); - } - - @Override - public void run() { - setPriority(Thread.MIN_PRIORITY); - while (true) { - Logger.d(TAG, "DownloadFileThread iteration"); - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - return; - } - DownloadBlock block = fetchBlock(); - if (block == null) { - synchronized (threadLocker) { - try { - threadLocker.wait(); - continue; - } catch (InterruptedException e) { - Logger.e(TAG, e); - return; - } - } - } - - long start = System.nanoTime(); - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Starting"); - try { - TLFile file = api.doGetFile(block.task.dcId, block.task.location, block.index * block.task.blockSize, block.task.blockSize); - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Downloaded in " + (System.nanoTime() - start) / (1000 * 1000L) + " ms"); - onBlockDownloaded(block, file.getBytes()); - } catch (IOException e) { - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.taskId + "| Failure"); - Logger.e(TAG, e); - onBlockFailure(block); - } - } - } - } -} diff --git a/src/org/telegram/api/engine/file/UploadListener.java b/src/org/telegram/api/engine/file/UploadListener.java deleted file mode 100644 index b0611cd..0000000 --- a/src/org/telegram/api/engine/file/UploadListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.telegram.api.engine.file; - -/** - * Created by ex3ndr on 19.11.13. - */ -public interface UploadListener { - public void onPartUploaded(int percent, int downloadedSize); -} diff --git a/src/org/telegram/api/engine/file/Uploader.java b/src/org/telegram/api/engine/file/Uploader.java deleted file mode 100644 index 714e2f6..0000000 --- a/src/org/telegram/api/engine/file/Uploader.java +++ /dev/null @@ -1,413 +0,0 @@ -package org.telegram.api.engine.file; - -import org.telegram.api.engine.Logger; -import org.telegram.api.engine.TelegramApi; -import org.telegram.mtproto.secure.CryptoUtils; -import org.telegram.mtproto.secure.Entropy; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by ex3ndr on 19.11.13. - */ -public class Uploader { - - private static final int KB = 1024; - private static final int MB = 1024 * KB; - - private final AtomicInteger fileIds = new AtomicInteger(1); - - public static final int FILE_QUEUED = 0; - public static final int FILE_IN_PROGRESS = 1; - public static final int FILE_COMPLETED = 2; - public static final int FILE_CANCELED = 3; - - private static final int BLOCK_QUEUED = 0; - private static final int BLOCK_DOWNLOADING = 1; - private static final int BLOCK_COMPLETED = 2; - - private static final int PARALLEL_DOWNLOAD_COUNT = 2; - - private static final int PARALLEL_PARTS_COUNT = 4; - - private static final int[] BLOCK_SIZES = new int[]{8 * KB, 16 * KB, 32 * KB, 64 * KB, 128 * KB, 256 * KB, 512 * KB}; - - private static final long DEFAULT_DELAY = 15 * 1000; - - private static final int BIG_FILE_MIN = 10 * 1024 * 1024; - - private static final int MAX_BLOCK_COUNT = 3000; - - private final String TAG; - - private TelegramApi api; - - private ArrayList tasks = new ArrayList(); - - private ArrayList threads = new ArrayList(); - - private final Object threadLocker = new Object(); - - private Random rnd = new Random(); - - public Uploader(TelegramApi api) { - this.TAG = api.toString() + "#Uploader"; - this.api = api; - - for (int i = 0; i < PARALLEL_PARTS_COUNT; i++) { - UploadFileThread thread = new UploadFileThread(); - thread.start(); - threads.add(thread); - } - } - - public TelegramApi getApi() { - return api; - } - - private synchronized UploadTask getTask(int taskId) { - for (UploadTask task : tasks) { - if (task.taskId == taskId) { - return task; - } - } - return null; - } - - public synchronized void cancelTask(int taskId) { - UploadTask task = getTask(taskId); - if (task != null && task.state != FILE_COMPLETED) { - task.state = FILE_CANCELED; - Logger.d(TAG, "File #" + task.taskId + "| Canceled"); - } - updateFileQueueStates(); - } - - public synchronized int getTaskState(int taskId) { - UploadTask task = getTask(taskId); - if (task != null) { - return task.state; - } - - return FILE_CANCELED; - } - - public void waitForTask(int taskId) { - while (true) { - int state = getTaskState(taskId); - if ((state == FILE_COMPLETED) || (state == FILE_CANCELED)) { - return; - } - synchronized (threadLocker) { - try { - threadLocker.wait(DEFAULT_DELAY); - } catch (InterruptedException e) { - Logger.e(TAG, e); - return; - } - } - } - } - - public UploadResult getUploadResult(int taskId) { - UploadTask task = getTask(taskId); - if (task == null) { - return null; - } - if (task.state != FILE_COMPLETED) { - return null; - } - - return new UploadResult(task.uniqId, task.blocks.length, task.hash, task.usedBigFile); - } - - public synchronized int requestTask(String srcFile, UploadListener listener) { - UploadTask task = new UploadTask(); - task.taskId = fileIds.getAndIncrement(); - task.uniqId = Entropy.generateRandomId(); - task.listener = listener; - task.srcFile = srcFile; - try { - task.file = new RandomAccessFile(srcFile, "r"); - task.size = (int) task.file.length(); - if (task.size >= BIG_FILE_MIN) { - task.usedBigFile = true; - Logger.d(TAG, "File #" + task.uniqId + "| Using big file method"); - } else { - task.usedBigFile = false; - } - long start = System.currentTimeMillis(); - Logger.d(TAG, "File #" + task.uniqId + "| Calculating hash"); - task.hash = CryptoUtils.MD5(task.file); - Logger.d(TAG, "File #" + task.uniqId + "| Hash " + task.hash + " in " + (System.currentTimeMillis() - start) + " ms"); - } catch (FileNotFoundException e) { - Logger.e(TAG, e); - } catch (IOException e) { - Logger.e(TAG, e); - } - - task.blockSize = BLOCK_SIZES[BLOCK_SIZES.length - 1]; - for (int size : BLOCK_SIZES) { - int totalBlockCount = (int) Math.ceil(((double) task.size) / size); - if (totalBlockCount < MAX_BLOCK_COUNT) { - task.blockSize = size; - break; - } - } - - Logger.d(TAG, "File #" + task.uniqId + "| Using block size: " + task.blockSize); - - int totalBlockCount = (int) Math.ceil(((double) task.size) / task.blockSize); - task.blocks = new UploadBlock[totalBlockCount]; - for (int i = 0; i < totalBlockCount; i++) { - task.blocks[i] = new UploadBlock(); - task.blocks[i].task = task; - task.blocks[i].index = i; - task.blocks[i].state = BLOCK_QUEUED; - } - task.state = FILE_QUEUED; - task.queueTime = System.nanoTime(); - tasks.add(task); - - Logger.d(TAG, "File #" + task.uniqId + "| Requested"); - - updateFileQueueStates(); - - return task.taskId; - } - - private synchronized UploadTask[] getActiveTasks() { - ArrayList res = new ArrayList(); - for (UploadTask task : tasks) { - if (task.state == FILE_IN_PROGRESS) { - res.add(task); - } - } - return res.toArray(new UploadTask[res.size()]); - } - - private synchronized void updateFileQueueStates() { - UploadTask[] activeTasks = getActiveTasks(); - outer: - for (UploadTask task : activeTasks) { - for (UploadBlock block : task.blocks) { - if (block.state != BLOCK_COMPLETED) { - continue outer; - } - } - onTaskCompleted(task); - } - activeTasks = getActiveTasks(); - - int count = activeTasks.length; - while (count < PARALLEL_DOWNLOAD_COUNT) { - long mintime = Long.MAX_VALUE; - UploadTask minTask = null; - for (UploadTask task : tasks) { - if (task.state == FILE_QUEUED && task.queueTime < mintime) { - minTask = task; - } - } - - if (minTask == null) { - break; - } - minTask.state = FILE_IN_PROGRESS; - Logger.d(TAG, "File #" + minTask.uniqId + "| Uploading"); - } - - synchronized (threadLocker) { - threadLocker.notifyAll(); - } - } - - private synchronized void onTaskCompleted(UploadTask task) { - if (task.state != FILE_COMPLETED) { - Logger.d(TAG, "File #" + task.uniqId + "| Completed in " + (System.nanoTime() - task.queueTime) / (1000 * 1000L) + " ms"); - task.state = FILE_COMPLETED; - try { - if (task.file != null) { - task.file.close(); - task.file = null; - } - } catch (IOException e) { - Logger.e(TAG, e); - } - } - updateFileQueueStates(); - } - - private synchronized UploadTask fetchTask() { - UploadTask[] activeTasks = getActiveTasks(); - if (activeTasks.length == 0) { - return null; - } else if (activeTasks.length == 1) { - return activeTasks[0]; - } else { - return activeTasks[rnd.nextInt(activeTasks.length)]; - } - } - - private synchronized UploadBlock fetchBlock() { - UploadTask task = fetchTask(); - if (task == null) { - return null; - } - - for (int i = 0; i < task.blocks.length; i++) { - if (task.blocks[i].state == BLOCK_QUEUED) { - task.blocks[i].state = BLOCK_DOWNLOADING; - byte[] block = new byte[Math.min(task.size - task.blockSize * i, task.blockSize)]; - try { - task.file.seek(task.blockSize * i); - task.file.readFully(block); - } catch (IOException e) { - Logger.e(TAG, e); - } - task.blocks[i].workData = block; - return task.blocks[i]; - } - } - - return null; - } - - private synchronized void onBlockUploaded(UploadBlock block) { - block.state = BLOCK_COMPLETED; - if (block.task.listener != null) { - int downloadedCount = 0; - for (UploadBlock b : block.task.blocks) { - if (b.state == BLOCK_COMPLETED) { - downloadedCount++; - } - } - int percent = downloadedCount * 100 / block.task.blocks.length; - block.task.listener.onPartUploaded(percent, downloadedCount); - } - updateFileQueueStates(); - } - - private synchronized void onBlockFailure(UploadBlock block) { - block.state = BLOCK_QUEUED; - updateFileQueueStates(); - } - - public static class UploadResult { - private long fileId; - private boolean usedBigFile; - private int partsCount; - private String hash; - - public UploadResult(long fileId, int partsCount, String hash, boolean usedBigFile) { - this.fileId = fileId; - this.partsCount = partsCount; - this.hash = hash; - this.usedBigFile = usedBigFile; - } - - public long getFileId() { - return fileId; - } - - public boolean isUsedBigFile() { - return usedBigFile; - } - - public int getPartsCount() { - return partsCount; - } - - public String getHash() { - return hash; - } - } - - private class UploadTask { - - public UploadListener listener; - - public boolean usedBigFile; - - public long uniqId; - - public int taskId; - - public int blockSize; - - public long queueTime; - - public int state; - - public int size; - - public UploadBlock[] blocks; - - public String srcFile; - - public RandomAccessFile file; - - public String hash; - } - - private class UploadBlock { - public UploadTask task; - public int state; - public int index; - public byte[] workData; - } - - private class UploadFileThread extends Thread { - - public UploadFileThread() { - setName("UploadFileThread#" + hashCode()); - } - - @Override - public void run() { - setPriority(Thread.MIN_PRIORITY); - while (true) { - Logger.d(TAG, "UploadFileThread iteration"); - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - return; - } - UploadBlock block = fetchBlock(); - if (block == null) { - synchronized (threadLocker) { - try { - threadLocker.wait(); - continue; - } catch (InterruptedException e) { - Logger.e(TAG, e); - return; - } - } - } - - long start = System.nanoTime(); - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Starting"); - try { - if (block.task.usedBigFile) { - api.doSaveBigFilePart(block.task.uniqId, block.index, block.task.blocks.length, block.workData); - } else { - api.doSaveFilePart(block.task.uniqId, block.index, block.workData); - } - block.workData = null; - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Uploaded in " + (System.nanoTime() - start) / (1000 * 1000L) + " ms"); - onBlockUploaded(block); - } catch (IOException e) { - Logger.d(TAG, "Block #" + block.index + " of #" + block.task.uniqId + "| Failure"); - Logger.e(TAG, e); - onBlockFailure(block); - } - } - } - } -} diff --git a/src/org/telegram/api/engine/storage/AbsApiState.java b/src/org/telegram/api/engine/storage/AbsApiState.java deleted file mode 100644 index ca19b52..0000000 --- a/src/org/telegram/api/engine/storage/AbsApiState.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.telegram.api.engine.storage; - -import org.telegram.api.TLConfig; -import org.telegram.mtproto.state.AbsMTProtoState; -import org.telegram.mtproto.state.ConnectionInfo; -import org.telegram.mtproto.state.KnownSalt; - -import java.util.HashMap; -import java.util.Map; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 10:19 - */ -public interface AbsApiState { - - int getPrimaryDc(); - - public void setPrimaryDc(int dc); - - boolean isAuthenticated(int dcId); - - void setAuthenticated(int dcId, boolean auth); - - void updateSettings(TLConfig config); - - - byte[] getAuthKey(int dcId); - - void putAuthKey(int dcId, byte[] key); - - ConnectionInfo[] getAvailableConnections(int dcId); - - AbsMTProtoState getMtProtoState(int dcId); - - void resetAuth(); - - void reset(); -} diff --git a/src/org/telegram/mtproto/CallWrapper.java b/src/org/telegram/mtproto/CallWrapper.java deleted file mode 100644 index 3d71efc..0000000 --- a/src/org/telegram/mtproto/CallWrapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.telegram.mtproto; - -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 3:56 - */ -public interface CallWrapper { - public TLObject wrapObject(TLMethod srcRequest); -} diff --git a/src/org/telegram/mtproto/MTProto.java b/src/org/telegram/mtproto/MTProto.java deleted file mode 100644 index 3f4de69..0000000 --- a/src/org/telegram/mtproto/MTProto.java +++ /dev/null @@ -1,561 +0,0 @@ -package org.telegram.mtproto; - -import com.droidkit.actors.*; -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.schedule.Scheduller; -import org.telegram.mtproto.secure.Entropy; -import org.telegram.mtproto.state.AbsMTProtoState; -import org.telegram.mtproto.state.KnownSalt; -import org.telegram.mtproto.time.TimeOverlord; -import org.telegram.mtproto.tl.*; -import org.telegram.mtproto.transport.*; -import org.telegram.mtproto.util.BytesCache; -import org.telegram.tl.DeserializeException; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.telegram.mtproto.secure.CryptoUtils.*; -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:14 - */ -public class MTProto { - - public static final int MODE_GENERAL = 0; - public static final int MODE_GENERAL_LOW_MODE = 1; - public static final int MODE_FILE = 2; - public static final int MODE_PUSH = 3; - - private static final AtomicInteger instanceIndex = new AtomicInteger(1000); - - private static final int MESSAGES_CACHE = 100; - private static final int MESSAGES_CACHE_MIN = 10; - - private static final int MAX_INTERNAL_FLOOD_WAIT = 10;//10 sec - - private static final int PING_INTERVAL_REQUEST = 60000;// 1 min - private static final int PING_INTERVAL = 75;//75 secs - - private static final int PING_PUSH_REQUEST = 9 * 60 * 1000; // 5 Min - - private static final int ERROR_MSG_ID_TOO_SMALL = 16; - private static final int ERROR_MSG_ID_TOO_BIG = 17; - private static final int ERROR_MSG_ID_BITS = 18; - private static final int ERROR_CONTAINER_MSG_ID_INCORRECT = 19; - private static final int ERROR_TOO_OLD = 20; - private static final int ERROR_SEQ_NO_TOO_SMALL = 32; - private static final int ERROR_SEQ_NO_TOO_BIG = 33; - private static final int ERROR_SEQ_EXPECTED_EVEN = 34; - private static final int ERROR_SEQ_EXPECTED_ODD = 35; - private static final int ERROR_BAD_SERVER_SALT = 48; - private static final int ERROR_BAD_CONTAINER = 64; - - private static final int PING_TIMEOUT = 60 * 1000; - private static final int RESEND_TIMEOUT = 60 * 1000; - - private static final int FUTURE_REQUEST_COUNT = 64; - private static final int FUTURE_MINIMAL = 5; - private static final long FUTURE_TIMEOUT = 5 * 60 * 60 * 1000;//5 min - private static final long FUTURE_NO_TIME_TIMEOUT = 15 * 60 * 1000;//15 sec - - private final String TAG; - private final int INSTANCE_INDEX; - - private MTProtoContext protoContext; - private ActorSystem actorSystem; - - private byte[] authKey; - private byte[] authKeyId; - private byte[] session; - private AbsMTProtoState state; - - private int desiredConnectionCount; - private TransportPool transportPool; - - private int mode = MODE_GENERAL; - private final Scheduller scheduller; - private final ArrayList receivedMessages = new ArrayList(); - private final ActorRef responseActor; - private final ActorRef actionsActor; - private MTProtoCallback callback; - - private boolean isClosed; - - public MTProto(AbsMTProtoState state, - MTProtoCallback callback, - CallWrapper callWrapper, - int connectionsCount, - int mode) { - this.INSTANCE_INDEX = instanceIndex.incrementAndGet(); - this.TAG = "MTProto#" + INSTANCE_INDEX; - this.mode = mode; - this.actorSystem = new ActorSystem(); - this.actorSystem.addDispatcher("connector"); - this.responseActor = actorSystem.actorOf(processor()); - this.actionsActor = actorSystem.actorOf(internal()); - - this.state = state; - this.callback = callback; - this.authKey = state.getAuthKey(); - this.authKeyId = substring(SHA1(authKey), 12, 8); - this.protoContext = MTProtoContext.getInstance(); - this.desiredConnectionCount = connectionsCount; - this.session = Entropy.generateSeed(8); - this.scheduller = new Scheduller(this, callWrapper); - this.scheduller.postMessage(new MTPing(Entropy.generateRandomId()), false, Long.MAX_VALUE); - - this.transportPool = new TransportTcpPool(this, new TransportPoolCallback() { - @Override - public void onMTMessage(MTMessage message) { - responseActor.send(message); - } - - @Override - public void onFastConfirm(int hash) { - // We might not send this to response actor for providing faster confirmation - int[] ids = scheduller.mapFastConfirm(hash); - for (int id : ids) { - MTProto.this.callback.onConfirmed(id); - } - } - }, desiredConnectionCount); - switch (mode) { - case MODE_GENERAL: - case MODE_PUSH: - transportPool.switchMode(TransportPool.MODE_DEFAULT); - break; - case MODE_GENERAL_LOW_MODE: - case MODE_FILE: - transportPool.switchMode(TransportPool.MODE_LOWMODE); - break; - - } - - this.actionsActor.sendOnce(new RequestPingDelay()); - this.actionsActor.sendOnce(new RequestSalt()); - } - - public AbsMTProtoState getState() { - return state; - } - - public void resetNetworkBackoff() { - this.transportPool.resetConnectionBackoff(); - this.actionsActor.sendOnce(new RequestPingDelay()); - } - - public void reloadConnectionInformation() { - this.transportPool.reloadConnectionInformation(); - this.actionsActor.sendOnce(new RequestPingDelay()); - } - - public int getInstanceIndex() { - return INSTANCE_INDEX; - } - - public Scheduller getScheduller() { - return scheduller; - } - - public byte[] getSession() { - return session; - } - - public byte[] getAuthKeyId() { - return authKeyId; - } - - public byte[] getAuthKey() { - return authKey; - } - - public ActorSystem getActorSystem() { - return actorSystem; - } - - public boolean isClosed() { - return isClosed; - } - - public int sendRpcMessage(TLMethod request, long timeout, boolean highPriority) { - int id = scheduller.postMessage(request, true, timeout, highPriority); - Logger.d(TAG, "sendMessage #" + id + " " + request.toString() + " with timeout " + timeout + " ms"); - return id; - } - - public void forgetMessage(int id) { - scheduller.forgetMessage(id); - } - - public void switchMode(int mode) { - if (this.mode != mode) { - this.mode = mode; - - switch (mode) { - case MODE_GENERAL: - case MODE_PUSH: - transportPool.switchMode(TransportPool.MODE_DEFAULT); - break; - case MODE_GENERAL_LOW_MODE: - case MODE_FILE: - transportPool.switchMode(TransportPool.MODE_LOWMODE); - break; - - } - - this.actionsActor.sendOnce(new RequestPingDelay()); - } - } - - public void close() { - if (!isClosed) { - this.isClosed = true; - // TODO: implement - // this.actorSystem.close(); - this.transportPool.close(); - } - } - - // Finding message type - private void onMTMessage(MTMessage mtMessage) { - if (mtMessage.getSeqNo() % 2 == 1) { - scheduller.confirmMessage(mtMessage.getMessageId()); - } - if (!needProcessing(mtMessage.getMessageId())) { - if (Logger.LOG_IGNORED) { - Logger.d(TAG, "Ignoring messages #" + mtMessage.getMessageId()); - } - return; - } - try { - TLObject intMessage = protoContext.deserializeMessage(new ByteArrayInputStream(mtMessage.getContent())); - onMTProtoMessage(mtMessage.getMessageId(), intMessage); - } catch (DeserializeException e) { - callback.onApiMessage(mtMessage.getContent(), this); - } catch (IOException e) { - Logger.e(TAG, e); - // ??? - } - } - - private void onMTProtoMessage(long msgId, TLObject object) { - Logger.d(TAG, "MTProtoMessage: " + object.toString()); - - if (object instanceof MTBadMessage) { - MTBadMessage badMessage = (MTBadMessage) object; - Logger.d(TAG, "BadMessage: " + badMessage.getErrorCode() + " #" + badMessage.getBadMsgId()); - scheduller.onMessageConfirmed(badMessage.getBadMsgId()); - long time = scheduller.getMessageIdGenerationTime(badMessage.getBadMsgId()); - if (time != 0) { - if (badMessage.getErrorCode() == ERROR_MSG_ID_TOO_BIG - || badMessage.getErrorCode() == ERROR_MSG_ID_TOO_SMALL) { - long delta = System.nanoTime() / 1000000 - time; - TimeOverlord.getInstance().onForcedServerTimeArrived((msgId >> 32) * 1000, delta); - if (badMessage.getErrorCode() == ERROR_MSG_ID_TOO_BIG) { - scheduller.resetMessageId(); - } - scheduller.resendAsNewMessage(badMessage.getBadMsgId()); - } else if (badMessage.getErrorCode() == ERROR_SEQ_NO_TOO_BIG || badMessage.getErrorCode() == ERROR_SEQ_NO_TOO_SMALL) { - if (scheduller.isMessageFromCurrentGeneration(badMessage.getBadMsgId())) { - Logger.d(TAG, "Resetting session"); - session = Entropy.generateSeed(8); - transportPool.onSessionChanged(session); - scheduller.resetSession(); - } - scheduller.resendAsNewMessage(badMessage.getBadMsgId()); - } else if (badMessage.getErrorCode() == ERROR_BAD_SERVER_SALT) { - long salt = ((MTBadServerSalt) badMessage).getNewSalt(); - // Sync time - long delta = System.nanoTime() / 1000000 - time; - TimeOverlord.getInstance().onMethodExecuted(badMessage.getBadMsgId(), msgId, delta); - state.badServerSalt(salt); - Logger.d(TAG, "Reschedule messages because bad_server_salt #" + badMessage.getBadMsgId()); - scheduller.resendAsNewMessage(badMessage.getBadMsgId()); - this.actionsActor.sendOnce(new RequestSalt()); - } else if (badMessage.getErrorCode() == ERROR_BAD_CONTAINER || - badMessage.getErrorCode() == ERROR_CONTAINER_MSG_ID_INCORRECT) { - scheduller.resendMessage(badMessage.getBadMsgId()); - } else if (badMessage.getErrorCode() == ERROR_TOO_OLD) { - scheduller.resendAsNewMessage(badMessage.getBadMsgId()); - } else { - if (Logger.LOG_IGNORED) { - Logger.d(TAG, "Ignored BadMsg #" + badMessage.getErrorCode() + " (" + badMessage.getBadMsgId() + ", " + badMessage.getBadMsqSeqno() + ")"); - } - scheduller.forgetMessageByMsgId(badMessage.getBadMsgId()); - } - } else { - if (Logger.LOG_IGNORED) { - Logger.d(TAG, "Unknown package #" + badMessage.getBadMsgId()); - } - } - } else if (object instanceof MTMsgsAck) { - MTMsgsAck ack = (MTMsgsAck) object; - String log = ""; - for (Long ackMsgId : ack.getMessages()) { - scheduller.onMessageConfirmed(ackMsgId); - if (log.length() > 0) { - log += ", "; - } - log += ackMsgId; - int id = scheduller.mapSchedullerId(ackMsgId); - if (id > 0) { - callback.onConfirmed(id); - } - } - Logger.d(TAG, "msgs_ack: " + log); - } else if (object instanceof MTRpcResult) { - MTRpcResult result = (MTRpcResult) object; - - Logger.d(TAG, "rpc_result: " + result.getMessageId()); - - int id = scheduller.mapSchedullerId(result.getMessageId()); - if (id > 0) { - int responseConstructor = readInt(result.getContent()); - if (responseConstructor == MTRpcError.CLASS_ID) { - try { - MTRpcError error = (MTRpcError) protoContext.deserializeMessage(result.getContent()); - BytesCache.getInstance().put(result.getContent()); - - if (error.getErrorCode() == 420) { - if (error.getErrorTag().startsWith("FLOOD_WAIT_")) { - // Secs - int delay = Integer.parseInt(error.getErrorTag().substring("FLOOD_WAIT_".length())); - if (delay <= MAX_INTERNAL_FLOOD_WAIT) { - scheduller.resendAsNewMessageDelayed(result.getMessageId(), delay * 1000); - return; - } - } - } - if (error.getErrorCode() == 401) { - if (error.getErrorTag().equals("AUTH_KEY_UNREGISTERED") || - error.getErrorTag().equals("AUTH_KEY_INVALID") || - error.getErrorTag().equals("USER_DEACTIVATED") || - error.getErrorTag().equals("SESSION_REVOKED") || - error.getErrorTag().equals("SESSION_EXPIRED")) { - Logger.w(TAG, "Auth key invalidated: " + error.getErrorTag()); - callback.onAuthInvalidated(this); - close(); - return; - } - } - - callback.onRpcError(id, error.getErrorCode(), error.getMessage(), this); - scheduller.forgetMessage(id); - } catch (IOException e) { - Logger.e(TAG, e); - return; - } - } else { - Logger.d(TAG, "rpc_result: " + result.getMessageId() + " #" + Integer.toHexString(responseConstructor)); - callback.onRpcResult(id, result.getContent(), this); - BytesCache.getInstance().put(result.getContent()); - scheduller.forgetMessage(id); - } - } else { - if (Logger.LOG_IGNORED) { - Logger.d(TAG, "ignored rpc_result: " + result.getMessageId()); - } - BytesCache.getInstance().put(result.getContent()); - } - scheduller.onMessageConfirmed(result.getMessageId()); - long time = scheduller.getMessageIdGenerationTime(result.getMessageId()); - if (time != 0) { - long delta = System.nanoTime() / 1000000 - time; - TimeOverlord.getInstance().onMethodExecuted(result.getMessageId(), msgId, delta); - } - } else if (object instanceof MTPong) { - MTPong pong = (MTPong) object; - if (Logger.LOG_PING) { - Logger.d(TAG, "pong: " + pong.getPingId()); - } - scheduller.onMessageConfirmed(pong.getMessageId()); - scheduller.forgetMessageByMsgId(pong.getMessageId()); - long time = scheduller.getMessageIdGenerationTime(pong.getMessageId()); - if (time != 0) { - long delta = System.nanoTime() / 1000000 - time; - TimeOverlord.getInstance().onMethodExecuted(pong.getMessageId(), msgId, delta); - } - } else if (object instanceof MTFutureSalts) { - MTFutureSalts salts = (MTFutureSalts) object; - scheduller.onMessageConfirmed(salts.getRequestId()); - scheduller.forgetMessageByMsgId(salts.getRequestId()); - - long time = scheduller.getMessageIdGenerationTime(salts.getRequestId()); - - if (time > 0) { - KnownSalt[] knownSalts = new KnownSalt[salts.getSalts().size()]; - for (int i = 0; i < knownSalts.length; i++) { - MTFutureSalt salt = salts.getSalts().get(i); - knownSalts[i] = new KnownSalt(salt.getValidSince(), salt.getValidUntil(), salt.getSalt()); - } - - long delta = System.nanoTime() / 1000000 - time; - TimeOverlord.getInstance().onForcedServerTimeArrived(salts.getNow(), delta); - state.mergeKnownSalts(salts.getNow(), knownSalts); - } - } else if (object instanceof MTMessageDetailedInfo) { - MTMessageDetailedInfo detailedInfo = (MTMessageDetailedInfo) object; - Logger.d(TAG, "msg_detailed_info: " + detailedInfo.getMsgId() + ", answer: " + detailedInfo.getAnswerMsgId()); - if (receivedMessages.contains(detailedInfo.getAnswerMsgId())) { - scheduller.confirmMessage(detailedInfo.getAnswerMsgId()); - } else { - int id = scheduller.mapSchedullerId(detailedInfo.getMsgId()); - if (id > 0) { - scheduller.postMessage(new MTNeedResendMessage(new long[]{detailedInfo.getAnswerMsgId()}), false, RESEND_TIMEOUT); - } else { - scheduller.confirmMessage(detailedInfo.getAnswerMsgId()); - scheduller.forgetMessageByMsgId(detailedInfo.getMsgId()); - } - } - } else if (object instanceof MTNewMessageDetailedInfo) { - MTNewMessageDetailedInfo detailedInfo = (MTNewMessageDetailedInfo) object; - Logger.d(TAG, "msg_new_detailed_info: " + detailedInfo.getAnswerMsgId()); - if (receivedMessages.contains(detailedInfo.getAnswerMsgId())) { - scheduller.confirmMessage(detailedInfo.getAnswerMsgId()); - } else { - scheduller.postMessage(new MTNeedResendMessage(new long[]{detailedInfo.getAnswerMsgId()}), false, RESEND_TIMEOUT); - } - } else if (object instanceof MTNewSessionCreated) { - callback.onSessionCreated(this); - } else { - if (Logger.LOG_IGNORED) { - Logger.d(TAG, "Ignored MTProto message " + object.toString()); - } - } - } - - private boolean needProcessing(long messageId) { - synchronized (receivedMessages) { - if (receivedMessages.contains(messageId)) { - return false; - } - - if (receivedMessages.size() > MESSAGES_CACHE_MIN) { - boolean isSmallest = true; - for (Long l : receivedMessages) { - if (messageId > l) { - isSmallest = false; - break; - } - } - - if (isSmallest) { - return false; - } - } - - while (receivedMessages.size() >= MESSAGES_CACHE - 1) { - receivedMessages.remove(0); - } - receivedMessages.add(messageId); - } - - return true; - } - - private ActorSelection internal() { - return new ActorSelection(Props.create(InternalActionsActor.class, new ActorCreator() { - @Override - public InternalActionsActor create() { - return new InternalActionsActor(MTProto.this); - } - }), "internal_" + INSTANCE_INDEX); - } - - private ActorSelection processor() { - return new ActorSelection(Props.create(ResponseActor.class, new ActorCreator() { - @Override - public ResponseActor create() { - return new ResponseActor(MTProto.this); - } - }), "response_" + INSTANCE_INDEX); - } - - private static class RequestSalt { - } - - private static class RequestPingDelay { - - } - - private static class InternalActionsActor extends Actor { - - private int lastPingMessage = -1; - private final MTProto proto; - - public InternalActionsActor(MTProto proto) { - this.proto = proto; - } - - @Override - public void onReceive(Object message) { - if (message instanceof RequestSalt) { - onRequestSaltsMessage(); - } else if (message instanceof RequestPingDelay) { - onPingDelayMessage(); - } - } - - public void onRequestSaltsMessage() { - // Logger.d(TAG, "Salt check timeout"); - if (TimeOverlord.getInstance().getTimeAccuracy() > 1000) { - // Logger.d(TAG, "Time is not accurate: " + TimeOverlord.getInstance().getTimeAccuracy()); - self().send(new RequestSalt(), FUTURE_NO_TIME_TIMEOUT); - return; - } - int count = proto.state.maximumCachedSalts((int) (TimeOverlord.getInstance().getServerTime() / 1000)); - if (count < FUTURE_MINIMAL) { - // Logger.d(TAG, "Too few actual salts: " + count + ", requesting news"); - proto.scheduller.postMessage(new MTGetFutureSalts(FUTURE_REQUEST_COUNT), false, FUTURE_TIMEOUT); - } - self().send(new RequestSalt(), FUTURE_TIMEOUT); - } - - public void onPingDelayMessage() { - if (lastPingMessage >= 0) { - proto.forgetMessage(lastPingMessage); - lastPingMessage = -1; - } - if (proto.mode == MODE_GENERAL) { - // Logger.d(TAG, "Ping delay disconnect for " + PING_INTERVAL + " sec"); - lastPingMessage = proto.scheduller.postMessage(new MTPingDelayDisconnect(Entropy.generateRandomId(), PING_INTERVAL), - false, PING_INTERVAL_REQUEST); - self().send(new RequestPingDelay(), PING_INTERVAL_REQUEST); - } else if (proto.mode == MODE_PUSH) { - lastPingMessage = proto.scheduller.postMessage(new MTPing(Entropy.generateRandomId()), false, PING_INTERVAL_REQUEST); - self().send(new RequestPingDelay(), PING_PUSH_REQUEST); - } - } - } - - private static class ResponseActor extends Actor { - private final MTProto proto; - - private ResponseActor(MTProto proto) { - this.proto = proto; - } - - @Override - public void onReceive(Object message) { - if (message instanceof MTMessage) { - MTMessage mtMessage = (MTMessage) message; - proto.onMTMessage(mtMessage); - BytesCache.getInstance().put(mtMessage.getContent()); - } - } - } - - @Override - public String toString() { - return "mtproto#" + INSTANCE_INDEX; - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/MTProtoCallback.java b/src/org/telegram/mtproto/MTProtoCallback.java deleted file mode 100644 index fa6e7c0..0000000 --- a/src/org/telegram/mtproto/MTProtoCallback.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.telegram.mtproto; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 04.11.13 - * Time: 22:11 - */ -public interface MTProtoCallback { - public void onSessionCreated(MTProto proto); - - public void onAuthInvalidated(MTProto proto); - - public void onApiMessage(byte[] message, MTProto proto); - - public void onRpcResult(int callId, byte[] response, MTProto proto); - - public void onRpcError(int callId, int errorCode, String message, MTProto proto); - - public void onConfirmed(int callId); -} diff --git a/src/org/telegram/mtproto/ServerException.java b/src/org/telegram/mtproto/ServerException.java deleted file mode 100644 index ab1a9b4..0000000 --- a/src/org/telegram/mtproto/ServerException.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.telegram.mtproto; - -import java.io.IOException; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:47 - */ -public class ServerException extends IOException { - public ServerException() { - } - - public ServerException(String s) { - super(s); - } - - public ServerException(String s, Throwable throwable) { - super(s, throwable); - } - - public ServerException(Throwable throwable) { - super(throwable); - } -} diff --git a/src/org/telegram/mtproto/TransportSecurityException.java b/src/org/telegram/mtproto/TransportSecurityException.java deleted file mode 100644 index 87f5204..0000000 --- a/src/org/telegram/mtproto/TransportSecurityException.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.telegram.mtproto; - -import java.io.IOException; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:44 - */ -public class TransportSecurityException extends IOException { - public TransportSecurityException() { - } - - public TransportSecurityException(String s) { - super(s); - } - - public TransportSecurityException(String s, Throwable throwable) { - super(s, throwable); - } - - public TransportSecurityException(Throwable throwable) { - super(throwable); - } -} diff --git a/src/org/telegram/mtproto/backoff/ExponentalBackoff.java b/src/org/telegram/mtproto/backoff/ExponentalBackoff.java deleted file mode 100644 index 31593ae..0000000 --- a/src/org/telegram/mtproto/backoff/ExponentalBackoff.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.telegram.mtproto.backoff; - -import org.telegram.mtproto.log.Logger; - -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by ex3ndr on 27.12.13. - */ -public class ExponentalBackoff { - - private static final int MIN_DELAY = 100; - private static final int MAX_DELAY = 15000; - private static final int MAX_FAILURE_COUNT = 50; - - private Random rnd = new Random(); - - private AtomicInteger currentFailureCount = new AtomicInteger(); - - private final String TAG; - - public ExponentalBackoff(String logTag) { - this.TAG = logTag; - } - - public void onFailure() throws InterruptedException { - int val = currentFailureCount.incrementAndGet(); - if (val > 50) { - currentFailureCount.compareAndSet(val, MAX_FAILURE_COUNT); - val = MAX_FAILURE_COUNT; - } - - int delay = MIN_DELAY + ((MAX_DELAY - MIN_DELAY) / MAX_FAILURE_COUNT) * val; - - synchronized (this) { - Logger.d(TAG, "onFailure: wait " + delay + " ms"); - wait(delay); - } - } - - public void onFailureNoWait() { - Logger.d(TAG, "onFailureNoWait"); - int val = currentFailureCount.incrementAndGet(); - if (val > 50) { - currentFailureCount.compareAndSet(val, MAX_FAILURE_COUNT); - val = MAX_FAILURE_COUNT; - } - } - - public void onSuccess() { - Logger.d(TAG, "onSuccess"); - reset(); - } - - public void reset() { - Logger.d(TAG, "reset"); - currentFailureCount.set(0); - - synchronized (this) { - notifyAll(); - } - } -} diff --git a/src/org/telegram/mtproto/log/LogInterface.java b/src/org/telegram/mtproto/log/LogInterface.java deleted file mode 100644 index 401a13b..0000000 --- a/src/org/telegram/mtproto/log/LogInterface.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.telegram.mtproto.log; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 10.11.13 - * Time: 2:11 - */ -public interface LogInterface { - void w(String tag, String message); - - void d(String tag, String message); - - void e(String tag, Throwable t); -} diff --git a/src/org/telegram/mtproto/log/Logger.java b/src/org/telegram/mtproto/log/Logger.java deleted file mode 100644 index 3ba2f6e..0000000 --- a/src/org/telegram/mtproto/log/Logger.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.telegram.mtproto.log; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 3:54 - */ -public class Logger { - - public static final boolean LOG_THREADS = true; - public static final boolean LOG_IGNORED = true; - public static final boolean LOG_PING = true; - - private static LogInterface logInterface; - - public static void registerInterface(LogInterface logInterface) { - Logger.logInterface = logInterface; - } - - public static void w(String tag, String message) { - if (logInterface != null) { - logInterface.w(tag, message); - } else { - System.out.println(tag + ":" + message); - } - } - - public static void d(String tag, String message) { - if (logInterface != null) { - logInterface.d(tag, message); - } else { - System.out.println(tag + ":" + message); - } - } - - public static void e(String tag, Throwable t) { - if (logInterface != null) { - logInterface.e(tag, t); - } else { - t.printStackTrace(); - } - } -} diff --git a/src/org/telegram/mtproto/pq/Authorizer.java b/src/org/telegram/mtproto/pq/Authorizer.java deleted file mode 100644 index 144ac11..0000000 --- a/src/org/telegram/mtproto/pq/Authorizer.java +++ /dev/null @@ -1,225 +0,0 @@ -package org.telegram.mtproto.pq; - -import org.telegram.mtproto.ServerException; -import org.telegram.mtproto.TransportSecurityException; -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.secure.CryptoUtils; -import org.telegram.mtproto.secure.Entropy; -import org.telegram.mtproto.secure.Keys; -import org.telegram.mtproto.secure.pq.PQSolver; -import org.telegram.mtproto.state.ConnectionInfo; -import org.telegram.mtproto.time.TimeOverlord; -import org.telegram.mtproto.tl.pq.*; -import org.telegram.mtproto.transport.ConnectionType; -import org.telegram.mtproto.transport.PlainTcpConnection; -import org.telegram.mtproto.transport.TransportRate; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; - -import static org.telegram.mtproto.secure.CryptoUtils.*; -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 4:11 - */ -public class Authorizer { - private static final String TAG = "Authorizer"; - private static final int AUTH_ATTEMPT_COUNT = 5; - private static final int AUTH_RETRY_COUNT = 5; - - private PlainTcpConnection context; - private TLInitContext initContext; - - public Authorizer() { - initContext = new TLInitContext(); - } - - private T executeMethod(TLMethod object) throws IOException { - long requestMessageId = TimeOverlord.getInstance().createWeakMessageId(); - long start = System.nanoTime(); - byte[] data = object.serialize(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - writeLong(0, out); // Empty AUTH_ID - writeLong(requestMessageId, out); // MessageID - writeInt(data.length, out); - writeByteArray(data, out); - byte[] response = context.executeMethod(out.toByteArray()); - ByteArrayInputStream in = new ByteArrayInputStream(response); - long authId = readLong(in); - if (authId != 0) { - throw new IOException("Auth id might be equal to zero"); - } - long messageId = readLong(in); - // TimeOverlord.getInstance().onMethodExecuted(requestMessageId, messageId, (System.nanoTime() - start) / 1000000); - int length = readInt(in); - byte[] messageResponse = readBytes(length, in); - return object.deserializeResponse(messageResponse, initContext); - } - - private PqAuth authAttempt() throws IOException { - // PQ-Auth start - byte[] nonce = Entropy.generateSeed(16); - ResPQ resPQ = executeMethod(new ReqPQ(nonce)); - byte[] serverNonce = resPQ.getServerNonce(); - - long fingerprint = 0; - Keys.Key publicKey = null; - outer: - for (Long srcFingerprint : resPQ.getFingerprints()) { - for (Keys.Key key : Keys.AVAILABLE_KEYS) { - if (srcFingerprint.equals(key.getFingerprint())) { - fingerprint = srcFingerprint; - publicKey = key; - break outer; - } - } - } - - if (fingerprint == 0) { - throw new IOException("Unknown public keys"); - } - - BigInteger pq = loadBigInt(resPQ.getPq()); - BigInteger p = null; - try { - long start = System.currentTimeMillis(); - p = PQSolver.solvePq(pq); - Logger.d(TAG, "Solved PQ in " + (System.currentTimeMillis() - start) + " ms"); - } catch (Exception e) { - throw new IOException(); - } - BigInteger q = pq.divide(p); - - byte[] newNonce = Entropy.generateSeed(32); - - PQInner inner = new PQInner(resPQ.getPq(), fromBigInt(p), fromBigInt(q), nonce, - serverNonce, newNonce); - - byte[] pqInner = inner.serialize(); - - // PQ INNER - byte[] hash = CryptoUtils.SHA1(pqInner); - byte[] seed = Entropy.generateSeed(255 - hash.length - pqInner.length); - byte[] dataWithHash = concat(hash, pqInner, seed); - - byte[] encrypted = CryptoUtils.RSA(dataWithHash, publicKey.getPublicKey(), publicKey.getExponent()); - - long start = System.nanoTime(); - ServerDhParams dhParams = executeMethod(new ReqDhParams(nonce, serverNonce, fromBigInt(p), fromBigInt(q), - fingerprint, encrypted)); - long dhParamsDuration = (System.nanoTime() - start) / (1000 * 1000); - - if (dhParams instanceof ServerDhFailure) { - ServerDhFailure hdFailure = (ServerDhFailure) dhParams; - if (arrayEq(hdFailure.getNewNonceHash(), SHA1(newNonce))) { - throw new ServerException("Received server_DH_params_fail#79cb045d"); - } else { - throw new TransportSecurityException("Received server_DH_params_fail#79cb045d with incorrect hash"); - } - } - - // PQ-Auth end - // DH-Auth start - ServerDhOk serverDhParams = (ServerDhOk) dhParams; - - byte[] encryptedAnswer = serverDhParams.getEncryptedAnswer(); - - byte[] tmpAesKey = concat(SHA1(newNonce, serverNonce), substring(SHA1(serverNonce, newNonce), 0, 12)); - byte[] tmpAesIv = concat(concat(substring(SHA1(serverNonce, newNonce), 12, 8), SHA1(newNonce, newNonce)), - substring(newNonce, 0, 4)); - - byte[] answer = AES256IGEDecrypt(encryptedAnswer, tmpAesIv, tmpAesKey); - ByteArrayInputStream stream = new ByteArrayInputStream(answer); - byte[] answerHash = readBytes(20, stream); // Hash - ServerDhInner dhInner = (ServerDhInner) initContext.deserializeMessage(stream); - if (!arrayEq(answerHash, SHA1(dhInner.serialize()))) { - throw new TransportSecurityException(); - } - - TimeOverlord.getInstance().onServerTimeArrived(dhInner.getServerTime() * 1000L, dhParamsDuration); - - for (int i = 0; i < AUTH_RETRY_COUNT; i++) { - BigInteger b = loadBigInt(Entropy.generateSeed(256)); - BigInteger g = new BigInteger(dhInner.getG() + ""); - BigInteger dhPrime = loadBigInt(dhInner.getDhPrime()); - BigInteger gb = g.modPow(b, dhPrime); - - BigInteger authKeyVal = loadBigInt(dhInner.getG_a()).modPow(b, dhPrime); - byte[] authKey = alignKeyZero(fromBigInt(authKeyVal), 256); - byte[] authAuxHash = substring(SHA1(authKey), 0, 8); - - ClientDhInner clientDHInner = new ClientDhInner(nonce, serverNonce, i, fromBigInt(gb)); - byte[] innerData = clientDHInner.serialize(); - byte[] innerDataWithHash = align(concat(SHA1(innerData), innerData), 16); - byte[] dataWithHashEnc = AES256IGEEncrypt(innerDataWithHash, tmpAesIv, tmpAesKey); - - DhGenResult result = executeMethod(new ReqSetDhClientParams(nonce, serverNonce, dataWithHashEnc)); - - if (result instanceof DhGenOk) { - byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{1}, authAuxHash), 4, 16); - - if (!arrayEq(result.getNewNonceHash(), newNonceHash)) - throw new TransportSecurityException(); - - long serverSalt = readLong(xor(substring(newNonce, 0, 8), substring(serverNonce, 0, 8)), 0); - - return new PqAuth(authKey, serverSalt, context.getSocket()); - } else if (result instanceof DhGenRetry) { - byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{2}, authAuxHash), 4, 16); - - if (!arrayEq(result.getNewNonceHash(), newNonceHash)) - throw new TransportSecurityException(); - - } else if (result instanceof DhGenFailure) { - byte[] newNonceHash = substring(SHA1(newNonce, new byte[]{3}, authAuxHash), 4, 16); - - if (!arrayEq(result.getNewNonceHash(), newNonceHash)) - throw new TransportSecurityException(); - - throw new ServerException(); - } - } - throw new ServerException(); - } - - public PqAuth doAuth(ConnectionInfo[] infos) { - TransportRate rate = new TransportRate(infos); - for (int i = 0; i < AUTH_ATTEMPT_COUNT; i++) { - ConnectionType connectionType = rate.tryConnection(); - try { - context = new PlainTcpConnection(connectionType.getHost(), connectionType.getPort()); - rate.onConnectionSuccess(connectionType.getId()); - } catch (IOException e) { - Logger.e(TAG, e); - rate.onConnectionFailure(connectionType.getId()); - continue; - } - - try { - return authAttempt(); - } catch (IOException e) { - Logger.e(TAG, e); - } finally { - if (context != null) { - context.destroy(); - context = null; - } - } - - try { - Thread.sleep(300); - } catch (InterruptedException e) { - return null; - } - } - return null; - } -} diff --git a/src/org/telegram/mtproto/pq/PqAuth.java b/src/org/telegram/mtproto/pq/PqAuth.java deleted file mode 100644 index 35428d4..0000000 --- a/src/org/telegram/mtproto/pq/PqAuth.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.telegram.mtproto.pq; - -import java.net.Socket; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:14 - */ -public class PqAuth { - private byte[] authKey; - private long serverSalt; - private Socket socket; - - public PqAuth(byte[] authKey, long serverSalt, Socket socket) { - this.authKey = authKey; - this.serverSalt = serverSalt; - this.socket = socket; - } - - public byte[] getAuthKey() { - return authKey; - } - - public long getServerSalt() { - return serverSalt; - } - - public Socket getSocket() { - return socket; - } -} diff --git a/src/org/telegram/mtproto/schedule/PrepareSchedule.java b/src/org/telegram/mtproto/schedule/PrepareSchedule.java deleted file mode 100644 index b4e4df9..0000000 --- a/src/org/telegram/mtproto/schedule/PrepareSchedule.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.telegram.mtproto.schedule; - -/** - * Created by ex3ndr on 29.12.13. - */ -public class PrepareSchedule { - private long delay; - private int[] allowedContexts; - private boolean doWait; - - public boolean isDoWait() { - return doWait; - } - - public void setDoWait(boolean doWait) { - this.doWait = doWait; - } - - public long getDelay() { - return delay; - } - - public void setDelay(long delay) { - this.delay = delay; - } - - public int[] getAllowedContexts() { - return allowedContexts; - } - - public void setAllowedContexts(int[] allowedContexts) { - this.allowedContexts = allowedContexts; - } -} diff --git a/src/org/telegram/mtproto/schedule/PreparedPackage.java b/src/org/telegram/mtproto/schedule/PreparedPackage.java deleted file mode 100644 index 14e9efa..0000000 --- a/src/org/telegram/mtproto/schedule/PreparedPackage.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.telegram.mtproto.schedule; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 19:59 - */ -public class PreparedPackage { - private boolean isHighPriority; - private int seqNo; - private long messageId; - private byte[] content; - - public PreparedPackage(int seqNo, long messageId, byte[] content, boolean isHighPriority) { - this.seqNo = seqNo; - this.messageId = messageId; - this.content = content; - this.isHighPriority = isHighPriority; - } - - public boolean isHighPriority() { - return isHighPriority; - } - - public int getSeqNo() { - return seqNo; - } - - public long getMessageId() { - return messageId; - } - - public byte[] getContent() { - return content; - } -} diff --git a/src/org/telegram/mtproto/schedule/Scheduller.java b/src/org/telegram/mtproto/schedule/Scheduller.java deleted file mode 100644 index ab5088c..0000000 --- a/src/org/telegram/mtproto/schedule/Scheduller.java +++ /dev/null @@ -1,627 +0,0 @@ -package org.telegram.mtproto.schedule; - -import org.telegram.mtproto.CallWrapper; -import org.telegram.mtproto.MTProto; -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.time.TimeOverlord; -import org.telegram.mtproto.tl.*; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:51 - */ -public class Scheduller { - - private final String TAG;// = "MTProtoScheduller"; - - // Share identity values across all connections to avoid collisions - private static final AtomicInteger messagesIds = new AtomicInteger(1); - private static final ConcurrentHashMap idGenerationTime = new ConcurrentHashMap(); - - // private static final int SCHEDULLER_TIMEOUT = 15 * 1000;//15 sec - private static final int SCHEDULLER_TIMEOUT = 24 * 60 * 60 * 1000;//24 hours - - private static final long CONFIRM_TIMEOUT = 60 * 1000;//60 sec - - private static final int MAX_WORKLOAD_SIZE = 1024; - private static final int BIG_MESSAGE_SIZE = 1024; - private static final long RETRY_TIMEOUT = 15 * 1000; - - private static final int MAX_ACK_COUNT = 16; - - private SortedMap messages = Collections.synchronizedSortedMap(new TreeMap()); - private HashSet currentMessageGeneration = new HashSet(); - private HashSet confirmedMessages = new HashSet(); - private CopyOnWriteArrayList schedullerListeners = new CopyOnWriteArrayList(); - - private long firstConfirmTime = 0; - - private long lastMessageId = 0; - private long lastDependId = 0; - private int seqNo = 0; - - private CallWrapper wrapper; - - public Scheduller(MTProto mtProto, CallWrapper wrapper) { - TAG = "MTProto#" + mtProto.getInstanceIndex() + "#Scheduller"; - this.wrapper = wrapper; - } - - public void addListener(SchedullerListener listener) { - schedullerListeners.remove(listener); - schedullerListeners.add(listener); - } - - public void removeListener(SchedullerListener listener) { - schedullerListeners.remove(listener); - } - - private void notifyChanged() { - for (SchedullerListener listener : schedullerListeners) { - listener.onSchedullerUpdated(this); - } - } - - private synchronized long generateMessageId() { - long messageId = TimeOverlord.getInstance().createWeakMessageId(); - if (messageId <= lastMessageId) { - messageId = lastMessageId = lastMessageId + 4; - } - while (idGenerationTime.containsKey(messageId)) { - messageId += 4; - } - lastMessageId = messageId; - idGenerationTime.put(messageId, getCurrentTime()); - currentMessageGeneration.add(messageId); - return messageId; - } - - private synchronized int generateSeqNoWeak() { - return seqNo * 2; - } - - private synchronized int generateSeqNo() { - int res = seqNo * 2 + 1; - seqNo++; - return res; - } - - private synchronized void generateParams(SchedullerPackage schedullerPackage) { - schedullerPackage.messageId = generateMessageId(); - schedullerPackage.seqNo = generateSeqNo(); - schedullerPackage.idGenerationTime = getCurrentTime(); - schedullerPackage.relatedMessageIds.add(schedullerPackage.messageId); - schedullerPackage.generatedMessageIds.add(schedullerPackage.messageId); - } - - private long getCurrentTime() { - return System.nanoTime() / 1000000; - } - - public long getMessageIdGenerationTime(long msgId) { - if (idGenerationTime.containsKey(msgId)) { - return idGenerationTime.get(msgId); - } - return 0; - } - - public int postMessageDelayed(TLObject object, boolean isRpc, long timeout, int delay, int contextId, boolean highPrioroty) { - int id = messagesIds.incrementAndGet(); - SchedullerPackage schedullerPackage = new SchedullerPackage(id); - schedullerPackage.object = object; - schedullerPackage.addTime = getCurrentTime(); - schedullerPackage.scheduleTime = schedullerPackage.addTime + delay; - schedullerPackage.expiresTime = schedullerPackage.scheduleTime + timeout; - schedullerPackage.ttlTime = schedullerPackage.scheduleTime + timeout * 2; - schedullerPackage.isRpc = isRpc; - schedullerPackage.queuedToChannel = contextId; - schedullerPackage.priority = highPrioroty ? PRIORITY_HIGH : PRIORITY_NORMAL; - schedullerPackage.isDepend = highPrioroty; - schedullerPackage.supportTag = object.toString(); - schedullerPackage.serverErrorCount = 0; - messages.put(id, schedullerPackage); - notifyChanged(); - return id; - } - - public int postMessage(TLObject object, boolean isApi, long timeout) { - return postMessageDelayed(object, isApi, timeout, 0, -1, false); - } - - public int postMessage(TLObject object, boolean isApi, long timeout, boolean highPrioroty) { - return postMessageDelayed(object, isApi, timeout, 0, -1, highPrioroty); - } - - public synchronized void prepareScheduller(PrepareSchedule prepareSchedule, int[] connectionIds) { - long time = getCurrentTime(); - - // Clear packages for unknown channels - outer: - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.queuedToChannel != -1) { - for (int id : connectionIds) { - if (schedullerPackage.queuedToChannel == id) { - continue outer; - } - } - forgetMessage(schedullerPackage.id); - } - } - - // If there are no connections provide default delay - if (connectionIds.length == 0) { - prepareSchedule.setDelay(SCHEDULLER_TIMEOUT); - prepareSchedule.setAllowedContexts(connectionIds); - prepareSchedule.setDoWait(true); - return; - } - - long minDelay = SCHEDULLER_TIMEOUT; - boolean allConnections = false; - boolean doWait = true; - HashSet supportedConnections = new HashSet(); - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - boolean isPendingPackage = false; - long packageTime = 0; - - if (schedullerPackage.state == STATE_QUEUED) { - isPendingPackage = true; - if (schedullerPackage.scheduleTime <= time) { - packageTime = 0; - } else { - packageTime = Math.max(schedullerPackage.scheduleTime - time, 0); - } - } else if (schedullerPackage.state == STATE_SENT) { - if (getCurrentTime() <= schedullerPackage.expiresTime) { - if (time - schedullerPackage.lastAttemptTime >= RETRY_TIMEOUT) { - isPendingPackage = true; - packageTime = 0; - } - } - } - - if (isPendingPackage) { - if (schedullerPackage.queuedToChannel == -1) { - allConnections = true; - } else { - supportedConnections.add(schedullerPackage.queuedToChannel); - } - - if (packageTime == 0) { - minDelay = 0; - doWait = false; - } else { - minDelay = Math.min(packageTime, minDelay); - } - } - } - prepareSchedule.setDoWait(doWait); - prepareSchedule.setDelay(minDelay); - - if (allConnections) { - prepareSchedule.setAllowedContexts(connectionIds); - } else { - Integer[] allowedBoxed = supportedConnections.toArray(new Integer[0]); - int[] allowed = new int[allowedBoxed.length]; - for (int i = 0; i < allowed.length; i++) { - allowed[i] = allowedBoxed[i]; - } - prepareSchedule.setAllowedContexts(allowed); - } - } - - public synchronized void registerFastConfirm(long msgId, int fastConfirm) { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - boolean contains = false; - for (Long relatedMsgId : schedullerPackage.relatedMessageIds) { - if (relatedMsgId == msgId) { - contains = true; - break; - } - } - if (contains) { - schedullerPackage.relatedFastConfirm.add(fastConfirm); - } - } - } - - public int mapSchedullerId(long msgId) { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.generatedMessageIds.contains(msgId)) { - return schedullerPackage.id; - } - } - return 0; - } - - public void resetMessageId() { - lastMessageId = 0; - lastDependId = 0; - } - - public void resetSession() { - lastMessageId = 0; - lastDependId = 0; - seqNo = 0; - currentMessageGeneration.clear(); - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - schedullerPackage.idGenerationTime = 0; - schedullerPackage.dependMessageId = 0; - schedullerPackage.messageId = 0; - schedullerPackage.seqNo = 0; - } - notifyChanged(); - } - - public boolean isMessageFromCurrentGeneration(long msgId) { - return currentMessageGeneration.contains(msgId); - } - - public void resendAsNewMessage(long msgId) { - resendAsNewMessageDelayed(msgId, 0); - } - - public void resendAsNewMessageDelayed(long msgId, int delay) { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.relatedMessageIds.contains(msgId)) { - schedullerPackage.idGenerationTime = 0; - schedullerPackage.dependMessageId = 0; - schedullerPackage.messageId = 0; - schedullerPackage.seqNo = 0; - schedullerPackage.state = STATE_QUEUED; - schedullerPackage.scheduleTime = getCurrentTime() + delay; - Logger.d(TAG, "Resending as new #" + schedullerPackage.id); - } - } - notifyChanged(); - } - - public void resendMessage(long msgId) { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.relatedMessageIds.contains(msgId)) { - // schedullerPackage.relatedMessageIds.clear(); - schedullerPackage.state = STATE_QUEUED; - schedullerPackage.lastAttemptTime = 0; - } - } - notifyChanged(); - } - - public int[] mapFastConfirm(int fastConfirm) { - ArrayList res = new ArrayList(); - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.state == STATE_SENT) { - if (schedullerPackage.relatedFastConfirm.contains(fastConfirm)) { - res.add(schedullerPackage.id); - } - } - } - int[] res2 = new int[res.size()]; - for (int i = 0; i < res2.length; i++) { - res2[i] = res.get(i); - } - return res2; - } - - public void onMessageConfirmed(long msgId) { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.state == STATE_SENT) { - if (schedullerPackage.relatedMessageIds.contains(msgId)) { - schedullerPackage.state = STATE_CONFIRMED; - } - } - } - } - - public void confirmMessage(long msgId) { - synchronized (confirmedMessages) { - confirmedMessages.add(msgId); - if (firstConfirmTime == 0) { - firstConfirmTime = getCurrentTime(); - } - } - } - - public synchronized void forgetMessageByMsgId(long msgId) { - int scId = mapSchedullerId(msgId); - if (scId > 0) { - forgetMessage(scId); - } - } - - public synchronized void forgetMessage(int id) { - Logger.d(TAG, "Forgetting message: #" + id); - messages.remove(id); - } - - private synchronized ArrayList actualPackages(int contextId) { - ArrayList foundedPackages = new ArrayList(); - long time = getCurrentTime(); - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.queuedToChannel != -1 && contextId != schedullerPackage.queuedToChannel) { - continue; - } - boolean isPendingPackage = false; - - if (schedullerPackage.ttlTime <= getCurrentTime()) { - forgetMessage(schedullerPackage.id); - continue; - } - - if (schedullerPackage.state == STATE_QUEUED) { - if (schedullerPackage.scheduleTime <= time) { - isPendingPackage = true; - } - } else if (schedullerPackage.state == STATE_SENT) { - if (getCurrentTime() <= schedullerPackage.expiresTime) { - if (getCurrentTime() - schedullerPackage.lastAttemptTime >= RETRY_TIMEOUT) { - isPendingPackage = true; - } - } - } - - if (isPendingPackage) { - if (schedullerPackage.serialized == null) { - try { - if (schedullerPackage.isRpc) { - schedullerPackage.serialized = wrapper.wrapObject((TLMethod) schedullerPackage.object).serialize(); - } else { - schedullerPackage.serialized = schedullerPackage.object.serialize(); - } - } catch (IOException e) { - Logger.e(TAG, e); - forgetMessage(schedullerPackage.id); - continue; - } - } - - foundedPackages.add(schedullerPackage); - } - } - return foundedPackages; - } - - public synchronized boolean hasRequests() { - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.isRpc) - return true; - } - - return false; - } - - - public synchronized PreparedPackage doSchedule(int contextId, boolean isInited) { - ArrayList foundedPackages = actualPackages(contextId); - - synchronized (confirmedMessages) { - if (foundedPackages.size() == 0 && - (confirmedMessages.size() <= MAX_ACK_COUNT || (System.nanoTime() - firstConfirmTime) < CONFIRM_TIMEOUT)) { - return null; - } - } - - boolean useHighPriority = false; - - for (SchedullerPackage p : foundedPackages) { - if (p.priority == PRIORITY_HIGH) { - useHighPriority = true; - break; - } - } - - ArrayList packages = new ArrayList(); - - if (useHighPriority) { - Logger.d("Scheduller", "Using high priority scheduling"); - int totalSize = 0; - for (SchedullerPackage p : foundedPackages) { - if (p.priority == PRIORITY_HIGH) { - packages.add(p); - totalSize += p.serialized.length; - if (totalSize > MAX_WORKLOAD_SIZE) { - break; - } - } - } - } else { - int totalSize = 0; - for (SchedullerPackage p : foundedPackages) { - packages.add(p); - Logger.d("Scheduller", "Prepare package: " + p.supportTag + " of size " + p.serialized.length); - totalSize += p.serialized.length; - Logger.d("Scheduller", "Total size: " + totalSize); - if (totalSize > MAX_WORKLOAD_SIZE) { - break; - } - } - } - - Logger.d(TAG, "Iteration: count: " + packages.size() + ", confirm:" + confirmedMessages.size()); - Logger.d(TAG, "Building package"); - if (foundedPackages.size() == 0 && confirmedMessages.size() != 0) { - Long[] msgIds; - synchronized (confirmedMessages) { - msgIds = confirmedMessages.toArray(new Long[confirmedMessages.size()]); - confirmedMessages.clear(); - } - MTMsgsAck ack = new MTMsgsAck(msgIds); - Logger.d(TAG, "Single msg_ack"); - try { - return new PreparedPackage(generateSeqNoWeak(), generateMessageId(), ack.serialize(), useHighPriority); - } catch (IOException e) { - Logger.e(TAG, e); - return null; - } - } else if (foundedPackages.size() == 1 && confirmedMessages.size() == 0) { - SchedullerPackage schedullerPackage = foundedPackages.get(0); - schedullerPackage.state = STATE_SENT; - if (schedullerPackage.idGenerationTime == 0) { - generateParams(schedullerPackage); - } - Logger.d(TAG, "Single package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + ", " + schedullerPackage.seqNo + ")"); - schedullerPackage.writtenToChannel = contextId; - schedullerPackage.lastAttemptTime = getCurrentTime(); - return new PreparedPackage(schedullerPackage.seqNo, schedullerPackage.messageId, schedullerPackage.serialized, useHighPriority); - } else { - MTMessagesContainer container = new MTMessagesContainer(); - if ((confirmedMessages.size() > 0 && !useHighPriority) || (!isInited)) { - try { - Long[] msgIds; - synchronized (confirmedMessages) { - msgIds = confirmedMessages.toArray(new Long[0]); - confirmedMessages.clear(); - } - MTMsgsAck ack = new MTMsgsAck(msgIds); - Logger.d(TAG, "Adding msg_ack: " + msgIds.length); - container.getMessages().add(new MTMessage(generateMessageId(), generateSeqNoWeak(), ack.serialize())); - } catch (IOException e) { - Logger.e(TAG, e); - } - } - for (SchedullerPackage schedullerPackage : packages) { - schedullerPackage.state = STATE_SENT; - if (schedullerPackage.idGenerationTime == 0) { - generateParams(schedullerPackage); - } - - if (schedullerPackage.isDepend) { - if (schedullerPackage.dependMessageId == 0) { - if (lastDependId > 0) { - schedullerPackage.dependMessageId = lastDependId; - } else { - schedullerPackage.dependMessageId = -1; - } - } - - lastDependId = schedullerPackage.messageId; - } - schedullerPackage.writtenToChannel = contextId; - schedullerPackage.lastAttemptTime = getCurrentTime(); - if (schedullerPackage.isDepend && schedullerPackage.dependMessageId > 0) { - - Logger.d(TAG, "Adding package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + " on " + schedullerPackage.dependMessageId + ", " + schedullerPackage.seqNo + ")"); - - MTInvokeAfter invokeAfter = new MTInvokeAfter(schedullerPackage.dependMessageId, schedullerPackage.serialized); - try { - container.getMessages().add(new MTMessage(schedullerPackage.messageId, schedullerPackage.seqNo, invokeAfter.serialize())); - } catch (IOException e) { - Logger.e(TAG, e); - // Never happens - } - } else { - Logger.d(TAG, "Adding package: #" + schedullerPackage.id + " " + schedullerPackage.supportTag + " (" + schedullerPackage.messageId + ", " + schedullerPackage.seqNo + ")"); - container.getMessages().add(new MTMessage(schedullerPackage.messageId, schedullerPackage.seqNo, schedullerPackage.serialized)); - } - } - - long containerMessageId = generateMessageId(); - int containerSeq = generateSeqNoWeak(); - - for (SchedullerPackage schedullerPackage : packages) { - schedullerPackage.relatedMessageIds.add(containerMessageId); - } - - Logger.d(TAG, "Sending Package (" + containerMessageId + ", " + containerSeq + ")"); - - try { - return new PreparedPackage(containerSeq, containerMessageId, container.serialize(), useHighPriority); - } catch (IOException e) { - // Might not happens - Logger.e(TAG, e); - return null; - } - } - } - - public synchronized void onConnectionDies(int connectionId) { - Logger.d(TAG, "Connection dies " + connectionId); - for (SchedullerPackage schedullerPackage : messages.values().toArray(new SchedullerPackage[0])) { - if (schedullerPackage.writtenToChannel != connectionId) { - continue; - } - - if (schedullerPackage.queuedToChannel != -1) { - Logger.d(TAG, "Removing: #" + schedullerPackage.id + " " + schedullerPackage.supportTag); - forgetMessage(schedullerPackage.id); - } else { - if (schedullerPackage.isRpc) { - if (schedullerPackage.state == STATE_CONFIRMED || schedullerPackage.state == STATE_QUEUED) { - Logger.d(TAG, "Re-schedule: #" + schedullerPackage.id + " " + schedullerPackage.supportTag); - schedullerPackage.state = STATE_QUEUED; - schedullerPackage.lastAttemptTime = 0; - } - } else { - if (schedullerPackage.state == STATE_SENT) { - Logger.d(TAG, "Re-schedule: #" + schedullerPackage.id + " " + schedullerPackage.supportTag); - schedullerPackage.state = STATE_QUEUED; - schedullerPackage.lastAttemptTime = 0; - } - } - - } - } - notifyChanged(); - } - - private static final int PRIORITY_HIGH = 1; - private static final int PRIORITY_NORMAL = 0; - - private static final int STATE_QUEUED = 0; - private static final int STATE_SENT = 1; - private static final int STATE_CONFIRMED = 2; - - private class SchedullerPackage { - - public SchedullerPackage(int id) { - this.id = id; - } - - public String supportTag; - - public int id; - - public TLObject object; - public byte[] serialized; - - public long addTime; - public long scheduleTime; - public long expiresTime; - public long ttlTime; - public long lastAttemptTime; - - public int writtenToChannel = -1; - - public int queuedToChannel = -1; - - public int state = STATE_QUEUED; - - public int priority = PRIORITY_NORMAL; - - public boolean isDepend; - - public boolean isSent; - - public long idGenerationTime; - public long dependMessageId; - public long messageId; - public int seqNo; - public HashSet relatedFastConfirm = new HashSet(); - public HashSet relatedMessageIds = new HashSet(); - public HashSet generatedMessageIds = new HashSet(); - - public int serverErrorCount; - - public boolean isRpc; - } -} diff --git a/src/org/telegram/mtproto/schedule/SchedullerListener.java b/src/org/telegram/mtproto/schedule/SchedullerListener.java deleted file mode 100644 index 7cbc609..0000000 --- a/src/org/telegram/mtproto/schedule/SchedullerListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.telegram.mtproto.schedule; - -/** - * Created by ex3ndr on 03.04.14. - */ -public interface SchedullerListener { - public void onSchedullerUpdated(Scheduller scheduller); -} diff --git a/src/org/telegram/mtproto/secure/CryptoUtils.java b/src/org/telegram/mtproto/secure/CryptoUtils.java deleted file mode 100644 index 83bcf38..0000000 --- a/src/org/telegram/mtproto/secure/CryptoUtils.java +++ /dev/null @@ -1,277 +0,0 @@ -package org.telegram.mtproto.secure; - -import org.telegram.mtproto.secure.aes.AESImplementation; -import org.telegram.mtproto.secure.aes.DefaultAESImplementation; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import java.io.*; -import java.math.BigInteger; -import java.security.*; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.RSAPublicKeySpec; - -/** - * Author: Korshakov Stepan - * Created: 18.07.13 3:54 - */ -public class CryptoUtils { - - private static final ThreadLocal md5 = new ThreadLocal() { - @Override - protected MessageDigest initialValue() { - MessageDigest crypt = null; - try { - crypt = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - return crypt; - } - }; - - private static final ThreadLocal sha1 = new ThreadLocal() { - @Override - protected MessageDigest initialValue() { - MessageDigest crypt = null; - try { - crypt = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - return crypt; - } - }; - - private static AESImplementation currentImplementation = new DefaultAESImplementation(); - - public static void setAESImplementation(AESImplementation implementation) { - currentImplementation = implementation; - } - - public static byte[] RSA(byte[] src, BigInteger key, BigInteger exponent) { - try { - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PublicKey publicKey = keyFactory.generatePublic(new RSAPublicKeySpec(key, exponent)); - Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - return cipher.doFinal(src); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - } - return null; - } - - public static void AES256IGEDecryptBig(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) { - currentImplementation.AES256IGEDecrypt(src, dest, len, iv, key); - } - - public static byte[] AES256IGEDecrypt(byte[] src, byte[] iv, byte[] key) { - byte[] res = new byte[src.length]; - currentImplementation.AES256IGEDecrypt(src, res, src.length, iv, key); - return res; - } - - public static void AES256IGEDecrypt(File src, File dest, byte[] iv, byte[] key) throws IOException { - currentImplementation.AES256IGEDecrypt(src.getAbsolutePath(), dest.getAbsolutePath(), iv, key); - } - - public static void AES256IGEEncrypt(File src, File dest, byte[] iv, byte[] key) throws IOException { - currentImplementation.AES256IGEEncrypt(src.getAbsolutePath(), dest.getAbsolutePath(), iv, key); - } - - public static byte[] AES256IGEEncrypt(byte[] src, byte[] iv, byte[] key) { - byte[] res = new byte[src.length]; - currentImplementation.AES256IGEEncrypt(src, res, src.length, iv, key); - return res; - } - - public static String MD5(byte[] src) { - try { - MessageDigest crypt = MessageDigest.getInstance("MD5"); - crypt.reset(); - crypt.update(src); - return ToHex(crypt.digest()); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - - return null; - } - - public static String MD5(RandomAccessFile randomAccessFile) { - try { - MessageDigest crypt = md5.get(); - crypt.reset(); - byte[] block = new byte[8 * 1024]; - for (int i = 0; i < randomAccessFile.length(); i += 8 * 1024) { - int len = (int) Math.min(block.length, randomAccessFile.length() - i); - randomAccessFile.readFully(block, 0, len); - crypt.update(block, 0, len); - } - return ToHex(crypt.digest()); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; - } - - public static byte[] MD5Raw(byte[] src) { - MessageDigest crypt = md5.get(); - crypt.reset(); - crypt.update(src); - return crypt.digest(); - } - - public static String ToHex(byte[] src) { - String res = ""; - for (int i = 0; i < src.length; i++) { - res += String.format("%02X", src[i] & 0xFF); - } - return res.toLowerCase(); - } - - public static byte[] SHA1(InputStream in) throws IOException { - MessageDigest crypt = sha1.get(); - crypt.reset(); - // Transfer bytes from in to out - byte[] buf = new byte[4 * 1024]; - int len; - while ((len = in.read(buf)) > 0) { - Thread.yield(); - // out.write(buf, 0, len); - crypt.update(buf, 0, len); - } - in.close(); - return crypt.digest(); - } - - public static byte[] SHA1(String fileName) throws IOException { - MessageDigest crypt = sha1.get(); - crypt.reset(); - FileInputStream in = new FileInputStream(fileName); - // Transfer bytes from in to out - byte[] buf = new byte[4 * 1024]; - int len; - while ((len = in.read(buf)) > 0) { - Thread.yield(); - // out.write(buf, 0, len); - crypt.update(buf, 0, len); - } - in.close(); - return crypt.digest(); - } - - public static byte[] SHA1(byte[] src) { - MessageDigest crypt = sha1.get(); - crypt.reset(); - crypt.update(src); - return crypt.digest(); - } - - public static byte[] SHA1(byte[]... src1) { - MessageDigest crypt = sha1.get(); - crypt.reset(); - for (int i = 0; i < src1.length; i++) { - crypt.update(src1[i]); - } - return crypt.digest(); - } - - public static boolean arrayEq(byte[] a, byte[] b) { - if (a.length != b.length) { - return false; - } - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) - return false; - } - return true; - } - - public static byte[] concat(byte[]... v) { - int len = 0; - for (int i = 0; i < v.length; i++) { - len += v[i].length; - } - byte[] res = new byte[len]; - int offset = 0; - for (int i = 0; i < v.length; i++) { - System.arraycopy(v[i], 0, res, offset, v[i].length); - offset += v[i].length; - } - return res; - } - - public static byte[] substring(byte[] src, int start, int len) { - byte[] res = new byte[len]; - System.arraycopy(src, start, res, 0, len); - return res; - } - - public static byte[] align(byte[] src, int factor) { - if (src.length % factor == 0) { - return src; - } - int padding = factor - src.length % factor; - - return concat(src, Entropy.generateSeed(padding)); - } - - public static byte[] alignKeyZero(byte[] src, int size) { - if (src.length == size) { - return src; - } - - if (src.length > size) { - return substring(src, src.length - size, size); - } else { - return concat(new byte[size - src.length], src); - } - } - - public static byte[] xor(byte[] a, byte[] b) { - byte[] res = new byte[a.length]; - for (int i = 0; i < a.length; i++) { - res[i] = (byte) (a[i] ^ b[i]); - } - return res; - } - - public static BigInteger loadBigInt(byte[] data) { - return new BigInteger(1, data); - } - - public static byte[] fromBigInt(BigInteger val) { - byte[] res = val.toByteArray(); - if (res[0] == 0) { - byte[] res2 = new byte[res.length - 1]; - System.arraycopy(res, 1, res2, 0, res2.length); - return res2; - } else { - return res; - } - } - - public static boolean isZero(byte[] src) { - for (int i = 0; i < src.length; i++) { - if (src[i] != 0) { - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/secure/Entropy.java b/src/org/telegram/mtproto/secure/Entropy.java deleted file mode 100644 index 4f86929..0000000 --- a/src/org/telegram/mtproto/secure/Entropy.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.telegram.mtproto.secure; - -import java.security.SecureRandom; - -import static org.telegram.mtproto.secure.CryptoUtils.xor; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 4:05 - */ -public final class Entropy { - private static SecureRandom random = new SecureRandom(); - - public static byte[] generateSeed(int size) { - synchronized (random) { - return random.generateSeed(size); - } - } - - public static byte[] generateSeed(byte[] sourceSeed) { - synchronized (random) { - return xor(random.generateSeed(sourceSeed.length), sourceSeed); - } - } - - public static long generateRandomId() { - synchronized (random) { - return random.nextLong(); - } - } - - public static int randomInt() { - synchronized (random) { - return random.nextInt(); - } - } - - public static void feedEntropy(byte[] data) { - synchronized (random) { - random.setSeed(data); - } - } -} diff --git a/src/org/telegram/mtproto/secure/KeyParameter.java b/src/org/telegram/mtproto/secure/KeyParameter.java deleted file mode 100644 index f548479..0000000 --- a/src/org/telegram/mtproto/secure/KeyParameter.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.telegram.mtproto.secure; - -public class KeyParameter { - private byte[] key; - - public KeyParameter(byte[] key) { - this(key, 0, key.length); - } - - public KeyParameter(byte[] key, int keyOff, int keyLen) { - this.key = new byte[keyLen]; - - System.arraycopy(key, keyOff, this.key, 0, keyLen); - } - - public byte[] getKey() { - return key; - } -} diff --git a/src/org/telegram/mtproto/secure/Keys.java b/src/org/telegram/mtproto/secure/Keys.java deleted file mode 100644 index 8fb4520..0000000 --- a/src/org/telegram/mtproto/secure/Keys.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.telegram.mtproto.secure; - -import java.math.BigInteger; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:04 - */ -public class Keys { - public static class Key { - private BigInteger publicKey; - private BigInteger exponent; - private long fingerprint; - - public Key(String publicKey, String exponent, long fingerprint) { - this.publicKey = new BigInteger(publicKey, 16); - this.exponent = new BigInteger(exponent, 16); - this.fingerprint = fingerprint; - } - - public BigInteger getPublicKey() { - return publicKey; - } - - public BigInteger getExponent() { - return exponent; - } - - public long getFingerprint() { - return fingerprint; - } - } - - public static final Key[] AVAILABLE_KEYS = new Key[]{ - new Key("0c6aeda78b02a251db4b6441031f467fa871faed32526c436524b1fb3b5dca28efb8c089dd1b46d92c895993d87108254951c5f001a0f055f3063dcd14d431a300eb9e29517e359a1c9537e5e87ab1b116faecf5d17546ebc21db234d9d336a693efcb2b6fbcca1e7d1a0be414dca408a11609b9c4269a920b09fed1f9a1597be02761430f09e4bc48fcafbe289054c99dba51b6b5eb7d9c3a2ab4e490545b4676bd620e93804bcac93bf94f73f92c729ca899477ff17625ef14a934d51dc11d5f8650a3364586b3a52fcff2fedec8a8406cac4e751705a472e55707e3c8cd5594342b119c6c3293532d85dbe9271ed54a2fd18b4dc79c04a30951107d5639397", - "010001", 0x9a996a1db11c729bL), - new Key("0b1066749655935f0a5936f517034c943bea7f3365a8931ae52c8bcb14856f004b83d26cf2839be0f22607470d67481771c1ce5ec31de16b20bbaa4ecd2f7d2ecf6b6356f27501c226984263edc046b89fb6d3981546b01d7bd34fedcfcc1058e2d494bda732ff813e50e1c6ae249890b225f82b22b1e55fcb063dc3c0e18e91c28d0c4aa627dec8353eee6038a95a4fd1ca984eb09f94aeb7a2220635a8ceb450ea7e61d915cdb4eecedaa083aa3801daf071855ec1fb38516cb6c2996d2d60c0ecbcfa57e4cf1fb0ed39b2f37e94ab4202ecf595e167b3ca62669a6da520859fb6d6c6203dfdfc79c75ec3ee97da8774b2da903e3435f2cd294670a75a526c1", - "010001", 0xb05b2a6f70cdea78L), - new Key("0c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f", - "010001", 0xc3b42b026ce86b21L), - new Key("0c2a8c55b4a62e2b78a19b91cf692bcdc4ba7c23fe4d06f194e2a0c30f6d9996f7d1a2bcc89bc1ac4333d44359a6c433252d1a8402d9970378b5912b75bc8cc3fa76710a025bcb9032df0b87d7607cc53b928712a174ea2a80a8176623588119d42ffce40205c6d72160860d8d80b22a8b8651907cf388effbef29cd7cf2b4eb8a872052da1351cfe7fec214ce48304ea472bd66329d60115b3420d08f6894b0410b6ab9450249967617670c932f7cbdb5d6fbcce1e492c595f483109999b2661fcdeec31b196429b7834c7211a93c6789d9ee601c18c39e521fda9d7264e61e518add6f0712d2d5228204b851e13c4f322e5c5431c3b7f31089668486aadc59f", - "010001", 0x71e025b6c76033e3L) - }; -} diff --git a/src/org/telegram/mtproto/secure/aes/AESFastEngine.java b/src/org/telegram/mtproto/secure/aes/AESFastEngine.java deleted file mode 100644 index 17a61fa..0000000 --- a/src/org/telegram/mtproto/secure/aes/AESFastEngine.java +++ /dev/null @@ -1,833 +0,0 @@ -package org.telegram.mtproto.secure.aes; - -import org.telegram.mtproto.secure.KeyParameter; - -/** - * an implementation of the AES (Rijndael), from FIPS-197. - *

- * For further details see: http://csrc.nist.gov/encryption/aes/. - *

- * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at - * http://fp.gladman.plus.com/cryptography_technology/rijndael/ - *

- * There are three levels of tradeoff of speed vs memory - * Because java has no preprocessor, they are written as three separate classes from which to choose - *

- * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption - * and 4 for decryption. - *

- * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, - * adding 12 rotate operations per round to compute the values contained in the other tables from - * the contents of the first - *

- * The slowest version uses no static tables at all and computes the values in each round - *

- * This file contains the fast version with 8Kbytes of static tables for round precomputation - */ -public class AESFastEngine { - // The S box - private static final byte[] S = { - (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, (byte) 111, (byte) 197, - (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, (byte) 118, - (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240, - (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, - (byte) 183, (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, - (byte) 52, (byte) 165, (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, - (byte) 4, (byte) 199, (byte) 35, (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, - (byte) 7, (byte) 18, (byte) 128, (byte) 226, (byte) 235, (byte) 39, (byte) 178, (byte) 117, - (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, (byte) 110, (byte) 90, (byte) 160, - (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, (byte) 47, (byte) 132, - (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, (byte) 91, - (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207, - (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, - (byte) 69, (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, - (byte) 81, (byte) 163, (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, - (byte) 188, (byte) 182, (byte) 218, (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, - (byte) 205, (byte) 12, (byte) 19, (byte) 236, (byte) 95, (byte) 151, (byte) 68, (byte) 23, - (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, (byte) 93, (byte) 25, (byte) 115, - (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, (byte) 144, (byte) 136, - (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, (byte) 219, - (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92, - (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, - (byte) 231, (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, - (byte) 108, (byte) 86, (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, - (byte) 186, (byte) 120, (byte) 37, (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, - (byte) 232, (byte) 221, (byte) 116, (byte) 31, (byte) 75, (byte) 189, (byte) 139, (byte) 138, - (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, (byte) 3, (byte) 246, (byte) 14, - (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, (byte) 29, (byte) 158, - (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, (byte) 148, - (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223, - (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, - (byte) 65, (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, - }; - - // The inverse S-box - private static final byte[] Si = { - (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165, (byte) 56, - (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251, - (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, - (byte) 52, (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, - (byte) 84, (byte) 123, (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, - (byte) 238, (byte) 76, (byte) 149, (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, - (byte) 8, (byte) 46, (byte) 161, (byte) 102, (byte) 40, (byte) 217, (byte) 36, (byte) 178, - (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109, (byte) 139, (byte) 209, (byte) 37, - (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104, (byte) 152, (byte) 22, - (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182, (byte) 146, - (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218, - (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, - (byte) 144, (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, - (byte) 247, (byte) 228, (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, - (byte) 208, (byte) 44, (byte) 30, (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, - (byte) 193, (byte) 175, (byte) 189, (byte) 3, (byte) 1, (byte) 19, (byte) 138, (byte) 107, - (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79, (byte) 103, (byte) 220, (byte) 234, - (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180, (byte) 230, (byte) 115, - (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53, (byte) 133, - (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110, - (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, - (byte) 111, (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, - (byte) 252, (byte) 86, (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, - (byte) 154, (byte) 219, (byte) 192, (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, - (byte) 31, (byte) 221, (byte) 168, (byte) 51, (byte) 136, (byte) 7, (byte) 199, (byte) 49, - (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39, (byte) 128, (byte) 236, (byte) 95, - (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181, (byte) 74, (byte) 13, - (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156, (byte) 239, - (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176, - (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, - (byte) 23, (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, - (byte) 225, (byte) 105, (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, - }; - - // vector used in calculating key schedule (powers of x in GF(256)) - private static final int[] rcon = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, - 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91}; - - // precomputation tables of calculations for rounds - private static final int[] T0 = - { - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, - 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, - 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, - 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, - 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, - 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, - 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, - 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, - 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, - 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, - 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, - 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, - 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, - 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, - 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, - 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, - 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, - 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, - 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, - 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, - 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, - 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, - 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, - 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, - 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, - 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, - 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, - 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, - 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, - 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, - 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, - 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, - 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, - 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, - 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, - 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, - 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, - 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, - 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, - 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, - 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, - 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, - 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, - 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, - 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, - 0x3a16162c}; - - private static final int[] T1 = - { - 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, - 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, - 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, - 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, - 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, - 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, - 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, - 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, - 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, - 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, - 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, - 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, - 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, - 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, - 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, - 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, - 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, - 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, - 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, - 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, - 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, - 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, - 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, - 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, - 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, - 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, - 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, - 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, - 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, - 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, - 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, - 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, - 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, - 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, - 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, - 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, - 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, - 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, - 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, - 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, - 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, - 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, - 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, - 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, - 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, - 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, - 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, - 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, - 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, - 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, - 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, - 0x16162c3a}; - - private static final int[] T2 = - { - 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, - 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, - 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, - 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, - 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, - 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, - 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, - 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, - 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, - 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, - 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, - 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, - 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, - 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, - 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, - 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, - 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, - 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, - 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, - 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, - 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, - 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, - 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, - 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, - 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, - 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, - 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, - 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, - 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, - 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, - 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, - 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, - 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, - 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, - 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, - 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, - 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, - 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, - 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, - 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, - 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, - 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, - 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, - 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, - 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, - 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, - 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, - 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, - 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, - 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, - 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, - 0x162c3a16}; - - private static final int[] T3 = - { - 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, - 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, - 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, - 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, - 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, - 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, - 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, - 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, - 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, - 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, - 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, - 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, - 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, - 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, - 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, - 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, - 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, - 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, - 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, - 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, - 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, - 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, - 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, - 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, - 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, - 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, - 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, - 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, - 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, - 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, - 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, - 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, - 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, - 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, - 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, - 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, - 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, - 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, - 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, - 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, - 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, - 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, - 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, - 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, - 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, - 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, - 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, - 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, - 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, - 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, - 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, - 0x2c3a1616}; - - private static final int[] Tinv0 = - { - 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, - 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, - 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, - 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, - 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, - 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, - 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, - 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, - 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, - 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, - 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, - 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, - 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, - 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, - 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, - 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, - 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, - 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, - 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, - 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, - 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, - 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, - 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, - 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, - 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, - 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, - 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, - 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, - 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, - 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, - 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, - 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, - 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, - 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, - 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, - 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, - 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, - 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, - 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, - 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, - 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, - 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, - 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, - 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, - 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, - 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, - 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, - 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, - 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, - 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, - 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, - 0x4257b8d0}; - - private static final int[] Tinv1 = - { - 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, - 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, - 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, - 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, - 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, - 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, - 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, - 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, - 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, - 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, - 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, - 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, - 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, - 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, - 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, - 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, - 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, - 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, - 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, - 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, - 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, - 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, - 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, - 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, - 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, - 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, - 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, - 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, - 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, - 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, - 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, - 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, - 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, - 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, - 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, - 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, - 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, - 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, - 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, - 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, - 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, - 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, - 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, - 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, - 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, - 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, - 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, - 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, - 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, - 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de, - 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, - 0x57b8d042}; - - private static final int[] Tinv2 = - { - 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, - 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, - 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, - 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, - 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, - 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, - 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, - 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, - 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, - 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, - 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, - 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, - 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, - 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, - 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, - 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, - 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, - 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, - 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, - 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, - 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, - 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, - 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296, - 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, - 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, - 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, - 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, - 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, - 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, - 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, - 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, - 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, - 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, - 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, - 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, - 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, - 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, - 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, - 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, - 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, - 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, - 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, - 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, - 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, - 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, - 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, - 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, - 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, - 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, - 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3, - 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, - 0xb8d04257}; - - private static final int[] Tinv3 = - { - 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, - 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, - 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, - 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, - 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f, - 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, - 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, - 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, - 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, - 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, - 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, - 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, - 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, - 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, - 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, - 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, - 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, - 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, - 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, - 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, - 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, - 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, - 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee, - 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, - 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, - 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, - 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, - 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, - 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, - 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, - 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, - 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, - 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, - 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, - 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, - 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, - 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, - 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, - 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, - 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, - 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, - 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, - 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, - 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, - 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, - 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, - 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, - 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, - 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, - 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, - 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, - 0xd04257b8}; - - private int shift( - int r, - int shift) { - return (r >>> shift) | (r << -shift); - } - - /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ - - private static final int m1 = 0x80808080; - private static final int m2 = 0x7f7f7f7f; - private static final int m3 = 0x0000001b; - - private int FFmulX(int x) { - return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3)); - } - - /* - The following defines provide alternative definitions of FFmulX that might - give improved performance if a fast 32-bit multiply is not available. - - private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } - private static final int m4 = 0x1b1b1b1b; - private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } - - */ - - private int inv_mcol(int x) { - int f2 = FFmulX(x); - int f4 = FFmulX(f2); - int f8 = FFmulX(f4); - int f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24); - } - - - private int subWord(int x) { - return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24); - } - - /** - * Calculate the necessary round keys - * The number of calculations depends on key size and block size - * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits - * This code is written assuming those are the only possible values - */ - private int[][] generateWorkingKey( - byte[] key, - boolean forEncryption) { - int KC = key.length / 4; // key length in words - int t; - - if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length)) { - throw new IllegalArgumentException("Key length not 128/192/256 bits."); - } - - ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes - int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block - - // - // copy the key into the round key array - // - - t = 0; - int i = 0; - while (i < key.length) { - W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16) | (key[i + 3] << 24); - i += 4; - t++; - } - - // - // while not enough round key material calculated - // calculate new values - // - int k = (ROUNDS + 1) << 2; - for (i = KC; (i < k); i++) { - int temp = W[(i - 1) >> 2][(i - 1) & 3]; - if ((i % KC) == 0) { - temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1]; - } else if ((KC > 6) && ((i % KC) == 4)) { - temp = subWord(temp); - } - - W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp; - } - - if (!forEncryption) { - for (int j = 1; j < ROUNDS; j++) { - for (i = 0; i < 4; i++) { - W[j][i] = inv_mcol(W[j][i]); - } - } - } - - return W; - } - - private int ROUNDS; - private int[][] WorkingKey = null; - private int C0, C1, C2, C3; - private boolean forEncryption; - - private static final int BLOCK_SIZE = 16; - - /** - * default constructor - 128 bit block size. - */ - public AESFastEngine() { - } - - /** - * initialise an AES cipher. - * - * @param forEncryption whether or not we are for encryption. - * @param params the parameters required to set up the cipher. - * @throws IllegalArgumentException if the params argument is - * inappropriate. - */ - public void init( - boolean forEncryption, - KeyParameter params) { - - WorkingKey = generateWorkingKey(params.getKey(), forEncryption); - this.forEncryption = forEncryption; - } - - public String getAlgorithmName() { - return "AES"; - } - - public int getBlockSize() { - return BLOCK_SIZE; - } - - public int processBlock( - byte[] in, - int inOff, - byte[] out, - int outOff) { - if (WorkingKey == null) { - throw new IllegalStateException("AES engine not initialised"); - } - - if ((inOff + (32 / 2)) > in.length) { - throw new RuntimeException("input buffer too short"); - } - - if ((outOff + (32 / 2)) > out.length) { - throw new RuntimeException("output buffer too short"); - } - - if (forEncryption) { - unpackBlock(in, inOff); - encryptBlock(WorkingKey); - packBlock(out, outOff); - } else { - unpackBlock(in, inOff); - decryptBlock(WorkingKey); - packBlock(out, outOff); - } - - return BLOCK_SIZE; - } - - public void reset() { - } - - private void unpackBlock( - byte[] bytes, - int off) { - int index = off; - - C0 = (bytes[index++] & 0xff); - C0 |= (bytes[index++] & 0xff) << 8; - C0 |= (bytes[index++] & 0xff) << 16; - C0 |= bytes[index++] << 24; - - C1 = (bytes[index++] & 0xff); - C1 |= (bytes[index++] & 0xff) << 8; - C1 |= (bytes[index++] & 0xff) << 16; - C1 |= bytes[index++] << 24; - - C2 = (bytes[index++] & 0xff); - C2 |= (bytes[index++] & 0xff) << 8; - C2 |= (bytes[index++] & 0xff) << 16; - C2 |= bytes[index++] << 24; - - C3 = (bytes[index++] & 0xff); - C3 |= (bytes[index++] & 0xff) << 8; - C3 |= (bytes[index++] & 0xff) << 16; - C3 |= bytes[index++] << 24; - } - - private void packBlock( - byte[] bytes, - int off) { - int index = off; - - bytes[index++] = (byte) C0; - bytes[index++] = (byte) (C0 >> 8); - bytes[index++] = (byte) (C0 >> 16); - bytes[index++] = (byte) (C0 >> 24); - - bytes[index++] = (byte) C1; - bytes[index++] = (byte) (C1 >> 8); - bytes[index++] = (byte) (C1 >> 16); - bytes[index++] = (byte) (C1 >> 24); - - bytes[index++] = (byte) C2; - bytes[index++] = (byte) (C2 >> 8); - bytes[index++] = (byte) (C2 >> 16); - bytes[index++] = (byte) (C2 >> 24); - - bytes[index++] = (byte) C3; - bytes[index++] = (byte) (C3 >> 8); - bytes[index++] = (byte) (C3 >> 16); - bytes[index++] = (byte) (C3 >> 24); - } - - private void encryptBlock(int[][] KW) { - int r, r0, r1, r2, r3; - - C0 ^= KW[0][0]; - C1 ^= KW[0][1]; - C2 ^= KW[0][2]; - C3 ^= KW[0][3]; - - r = 1; - while (r < ROUNDS - 1) { - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3]; - C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0]; - C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1]; - C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2]; - C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3]; - } - - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3]; - - // the final round's table is a simple function of S so we don't use a whole other four tables for it - - C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0]; - C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1]; - C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2]; - C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3]; - - } - - private void decryptBlock(int[][] KW) { - int r0, r1, r2, r3; - - C0 ^= KW[ROUNDS][0]; - C1 ^= KW[ROUNDS][1]; - C2 ^= KW[ROUNDS][2]; - C3 ^= KW[ROUNDS][3]; - - int r = ROUNDS - 1; - - while (r > 1) { - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3]; - C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255] ^ KW[r][0]; - C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255] ^ KW[r][1]; - C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255] ^ KW[r][2]; - C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255] ^ KW[r--][3]; - } - - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r][3]; - - // the final round's table is a simple function of Si so we don't use a whole other four tables for it - - C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16) ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0]; - C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1]; - C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2]; - C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16) ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3]; - } -} diff --git a/src/org/telegram/mtproto/secure/aes/AESImplementation.java b/src/org/telegram/mtproto/secure/aes/AESImplementation.java deleted file mode 100644 index 512c038..0000000 --- a/src/org/telegram/mtproto/secure/aes/AESImplementation.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.telegram.mtproto.secure.aes; - -import java.io.IOException; - -/** - * Created by ex3ndr on 12.02.14. - */ -public abstract interface AESImplementation { - public void AES256IGEDecrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key); - - public void AES256IGEEncrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key); - - public void AES256IGEEncrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException; - - public void AES256IGEDecrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException; -} diff --git a/src/org/telegram/mtproto/secure/aes/DefaultAESImplementation.java b/src/org/telegram/mtproto/secure/aes/DefaultAESImplementation.java deleted file mode 100644 index 5363893..0000000 --- a/src/org/telegram/mtproto/secure/aes/DefaultAESImplementation.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.telegram.mtproto.secure.aes; - -import org.telegram.mtproto.secure.KeyParameter; - -import java.io.*; - -import static org.telegram.mtproto.secure.CryptoUtils.substring; - -/** - * Created by ex3ndr on 12.02.14. - */ -public class DefaultAESImplementation implements AESImplementation { - - @Override - public void AES256IGEDecrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) { - AESFastEngine engine = new AESFastEngine(); - engine.init(false, new KeyParameter(key)); - - int blocksCount = len / 16; - - byte[] curIvX = iv; - byte[] curIvY = iv; - int curIvXOffset = 16; - int curIvYOffset = 0; - - for (int i = 0; i < blocksCount; i++) { - int offset = i * 16; - - for (int j = 0; j < 16; j++) { - dest[offset + j] = (byte) (src[offset + j] ^ curIvX[curIvXOffset + j]); - } - engine.processBlock(dest, offset, dest, offset); - for (int j = 0; j < 16; j++) { - dest[offset + j] = (byte) (dest[offset + j] ^ curIvY[curIvYOffset + j]); - } - - curIvY = src; - curIvYOffset = offset; - curIvX = dest; - curIvXOffset = offset; - - if (i % 31 == 32) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - - @Override - public void AES256IGEEncrypt(byte[] src, byte[] dest, int len, byte[] iv, byte[] key) { - AESFastEngine engine = new AESFastEngine(); - engine.init(true, new KeyParameter(key)); - - int blocksCount = len / 16; - - byte[] curIvX = iv; - byte[] curIvY = iv; - int curIvXOffset = 16; - int curIvYOffset = 0; - - for (int i = 0; i < blocksCount; i++) { - - int offset = i * 16; - - for (int j = 0; j < 16; j++) { - dest[offset + j] = (byte) (src[offset + j] ^ curIvY[curIvYOffset + j]); - } - engine.processBlock(dest, offset, dest, offset); - for (int j = 0; j < 16; j++) { - dest[offset + j] = (byte) (dest[offset + j] ^ curIvX[curIvXOffset + j]); - } - - curIvX = src; - curIvXOffset = offset; - curIvY = dest; - curIvYOffset = offset; - } - } - - @Override - public void AES256IGEEncrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException { - - File src = new File(sourceFile); - File dest = new File(destFile); - - AESFastEngine engine = new AESFastEngine(); - engine.init(true, new KeyParameter(key)); - - byte[] curIvX = substring(iv, 16, 16); - byte[] curIvY = substring(iv, 0, 16); - - BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(src)); - BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest)); - byte[] buffer = new byte[16]; - int count; - while ((count = inputStream.read(buffer)) > 0) { - byte[] outData = new byte[16]; - for (int j = 0; j < 16; j++) { - outData[j] = (byte) (buffer[j] ^ curIvY[j]); - } - engine.processBlock(outData, 0, outData, 0); - for (int j = 0; j < 16; j++) { - outData[j] = (byte) (outData[j] ^ curIvX[j]); - } - - curIvX = buffer; - curIvY = outData; - buffer = new byte[16]; - - outputStream.write(outData); - } - outputStream.flush(); - outputStream.close(); - inputStream.close(); - } - - @Override - public void AES256IGEDecrypt(String sourceFile, String destFile, byte[] iv, byte[] key) throws IOException { - File src = new File(sourceFile); - File dest = new File(destFile); - - AESFastEngine engine = new AESFastEngine(); - engine.init(false, new KeyParameter(key)); - - byte[] curIvX = substring(iv, 16, 16); - byte[] curIvY = substring(iv, 0, 16); - - BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(src)); - BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest)); - byte[] buffer = new byte[16]; - int count; - while ((count = inputStream.read(buffer)) > 0) { - byte[] outData = new byte[16]; - for (int j = 0; j < 16; j++) { - outData[j] = (byte) (buffer[j] ^ curIvX[j]); - } - engine.processBlock(outData, 0, outData, 0); - for (int j = 0; j < 16; j++) { - outData[j] = (byte) (outData[j] ^ curIvY[j]); - } - - curIvY = buffer; - curIvX = outData; - buffer = new byte[16]; - - outputStream.write(outData); - } - outputStream.flush(); - outputStream.close(); - inputStream.close(); - } -} diff --git a/src/org/telegram/mtproto/secure/pq/PQImplementation.java b/src/org/telegram/mtproto/secure/pq/PQImplementation.java deleted file mode 100644 index efcdb43..0000000 --- a/src/org/telegram/mtproto/secure/pq/PQImplementation.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.telegram.mtproto.secure.pq; - -/** - * Created by ex3ndr on 12.02.14. - */ -public interface PQImplementation { - public long findDivider(long src); -} diff --git a/src/org/telegram/mtproto/secure/pq/PQLopatin.java b/src/org/telegram/mtproto/secure/pq/PQLopatin.java deleted file mode 100644 index 85c2921..0000000 --- a/src/org/telegram/mtproto/secure/pq/PQLopatin.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.telegram.mtproto.secure.pq; - -import java.util.Random; - -/** - * Created by ex3ndr on 12.02.14. - */ -public class PQLopatin implements PQImplementation { - @Override - public long findDivider(long src) { - return findSmallMultiplierLopatin(src); - } - - private long GCD(long a, long b) { - while (a != 0 && b != 0) { - while ((b & 1) == 0) { - b >>= 1; - } - while ((a & 1) == 0) { - a >>= 1; - } - if (a > b) { - a -= b; - } else { - b -= a; - } - } - return b == 0 ? a : b; - } - - private long findSmallMultiplierLopatin(long what) { - Random r = new Random(); - long g = 0; - int it = 0; - for (int i = 0; i < 3; i++) { - int q = (r.nextInt(128) & 15) + 17; - long x = r.nextInt(1000000000) + 1, y = x; - int lim = 1 << (i + 18); - for (int j = 1; j < lim; j++) { - ++it; - long a = x, b = x, c = q; - while (b != 0) { - if ((b & 1) != 0) { - c += a; - if (c >= what) { - c -= what; - } - } - a += a; - if (a >= what) { - a -= what; - } - b >>= 1; - } - x = c; - long z = x < y ? y - x : x - y; - g = GCD(z, what); - if (g != 1) { - break; - } - if ((j & (j - 1)) == 0) { - y = x; - } - } - if (g > 1) { - break; - } - } - - long p = what / g; - return Math.min(p, g); - } -} diff --git a/src/org/telegram/mtproto/secure/pq/PQSolver.java b/src/org/telegram/mtproto/secure/pq/PQSolver.java deleted file mode 100644 index d1c4455..0000000 --- a/src/org/telegram/mtproto/secure/pq/PQSolver.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.telegram.mtproto.secure.pq; - -import java.math.BigInteger; - -/** - * Created by ex3ndr on 12.02.14. - */ -public class PQSolver { - private static PQImplementation currentImplementation = new PQLopatin(); - - public static void setCurrentImplementation(PQImplementation implementation) { - currentImplementation = implementation; - } - - public static BigInteger solvePq(BigInteger src) { - return new BigInteger("" + currentImplementation.findDivider(src.longValue())); - } - - private PQSolver() { - - } -} diff --git a/src/org/telegram/mtproto/state/AbsMTProtoState.java b/src/org/telegram/mtproto/state/AbsMTProtoState.java deleted file mode 100644 index 3a9f107..0000000 --- a/src/org/telegram/mtproto/state/AbsMTProtoState.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.telegram.mtproto.state; - -import org.telegram.mtproto.time.TimeOverlord; - -import java.util.HashMap; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:15 - */ -public abstract class AbsMTProtoState { - - public abstract byte[] getAuthKey(); - - public abstract ConnectionInfo[] getAvailableConnections(); - - public abstract KnownSalt[] readKnownSalts(); - - protected abstract void writeKnownSalts(KnownSalt[] salts); - - public void mergeKnownSalts(int currentTime, KnownSalt[] salts) { - KnownSalt[] knownSalts = readKnownSalts(); - HashMap ids = new HashMap(); - for (KnownSalt s : knownSalts) { - if (s.getValidUntil() < currentTime) { - continue; - } - ids.put(s.getSalt(), s); - } - for (KnownSalt s : salts) { - if (s.getValidUntil() < currentTime) { - continue; - } - ids.put(s.getSalt(), s); - } - writeKnownSalts(ids.values().toArray(new KnownSalt[0])); - } - - public void addCurrentSalt(long salt) { - int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000); - mergeKnownSalts(time, new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)}); - } - - public void badServerSalt(long salt) { - int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000); - writeKnownSalts(new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)}); - } - - public void initialServerSalt(long salt) { - int time = (int) (TimeOverlord.getInstance().getServerTime() / 1000); - writeKnownSalts(new KnownSalt[]{new KnownSalt(time, time + 30 * 60, salt)}); - } - - public long findActualSalt(int time) { - KnownSalt[] knownSalts = readKnownSalts(); - for (KnownSalt salt : knownSalts) { - if (salt.getValidSince() <= time && time <= salt.getValidUntil()) { - return salt.getSalt(); - } - } - - return 0; - } - - public int maximumCachedSalts(int time) { - int count = 0; - for (KnownSalt salt : readKnownSalts()) { - if (salt.getValidSince() > time) { - count++; - } - } - return count; - } - - public int maximumCachedSaltsTime() { - int max = 0; - for (KnownSalt salt : readKnownSalts()) { - max = Math.max(max, salt.getValidUntil()); - } - return max; - } -} diff --git a/src/org/telegram/mtproto/state/ConnectionInfo.java b/src/org/telegram/mtproto/state/ConnectionInfo.java deleted file mode 100644 index c865848..0000000 --- a/src/org/telegram/mtproto/state/ConnectionInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.telegram.mtproto.state; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:26 - */ -public class ConnectionInfo { - private int id; - private int priority; - private String address; - private int port; - - public ConnectionInfo(int id, int priority, String address, int port) { - this.id = id; - this.priority = priority; - this.address = address; - this.port = port; - } - - public int getPriority() { - return priority; - } - - public String getAddress() { - return address; - } - - public int getPort() { - return port; - } - - public int getId() { - return id; - } -} diff --git a/src/org/telegram/mtproto/state/KnownSalt.java b/src/org/telegram/mtproto/state/KnownSalt.java deleted file mode 100644 index 268532a..0000000 --- a/src/org/telegram/mtproto/state/KnownSalt.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.telegram.mtproto.state; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:16 - */ -public class KnownSalt { - private int validSince; - private int validUntil; - private long salt; - - public KnownSalt(int validSince, int validUntil, long salt) { - this.validSince = validSince; - this.validUntil = validUntil; - this.salt = salt; - } - - public int getValidSince() { - return validSince; - } - - public int getValidUntil() { - return validUntil; - } - - public long getSalt() { - return salt; - } -} diff --git a/src/org/telegram/mtproto/state/MemoryProtoState.java b/src/org/telegram/mtproto/state/MemoryProtoState.java deleted file mode 100644 index 3b4caee..0000000 --- a/src/org/telegram/mtproto/state/MemoryProtoState.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.telegram.mtproto.state; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:21 - */ -public class MemoryProtoState extends AbsMTProtoState { - - private KnownSalt[] salts = new KnownSalt[0]; - - private String address; - private int port; - private byte[] authKey; - - public MemoryProtoState(byte[] authKey, String address, int port) { - this.authKey = authKey; - this.port = port; - this.address = address; - } - - @Override - public byte[] getAuthKey() { - return authKey; - } - - @Override - public ConnectionInfo[] getAvailableConnections() { - return new ConnectionInfo[]{new ConnectionInfo(0, 0, address, port)}; - } - - @Override - public KnownSalt[] readKnownSalts() { - return salts; - } - - @Override - protected void writeKnownSalts(KnownSalt[] salts) { - this.salts = salts; - } -} diff --git a/src/org/telegram/mtproto/time/TimeOverlord.java b/src/org/telegram/mtproto/time/TimeOverlord.java deleted file mode 100644 index e8bf1bb..0000000 --- a/src/org/telegram/mtproto/time/TimeOverlord.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.telegram.mtproto.time; - -import org.telegram.mtproto.log.Logger; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 02.11.13 - * Time: 21:35 - */ -public class TimeOverlord { - private static TimeOverlord instance; - - public static synchronized TimeOverlord getInstance() { - if (instance == null) { - instance = new TimeOverlord(); - } - return instance; - } - - private long nanotimeShift; - - private long timeAccuracy = Long.MAX_VALUE; - protected long timeDelta; - - private TimeOverlord() { - nanotimeShift = System.currentTimeMillis() - System.nanoTime() / 1000; - } - - public long createWeakMessageId() { - return (getServerTime() / 1000) << 32; - } - - public long getLocalTime() { - return System.currentTimeMillis(); - } - - public long getServerTime() { - return getLocalTime() + timeDelta; - } - - public long getTimeAccuracy() { - return timeAccuracy; - } - - public long getTimeDelta() { - return timeDelta; - } - - public void setTimeDelta(long timeDelta, long timeAccuracy) { - this.timeDelta = timeDelta; - this.timeAccuracy = timeAccuracy; - } - - public void onForcedServerTimeArrived(long serverTime, long duration) { - timeDelta = serverTime - getLocalTime(); - timeAccuracy = duration; - } - - public void onServerTimeArrived(long serverTime, long duration) { - if (duration < 0) { - return; - } - if (duration < timeAccuracy) { - timeDelta = serverTime - getLocalTime(); - timeAccuracy = duration; - } else if (Math.abs(getLocalTime() - serverTime) > (duration / 2 + timeAccuracy / 2)) { - timeDelta = serverTime - getLocalTime(); - timeAccuracy = duration; - } - } - - public void onMethodExecuted(long sentId, long responseId, long duration) { - if (duration < 0) { - return; - } - - onServerTimeArrived((responseId >> 32) * 1000, duration); - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/tl/MTBadMessage.java b/src/org/telegram/mtproto/tl/MTBadMessage.java deleted file mode 100644 index 59f15d2..0000000 --- a/src/org/telegram/mtproto/tl/MTBadMessage.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 21:40 - */ -public abstract class MTBadMessage extends TLObject { - protected long badMsgId; - protected int badMsqSeqno; - protected int errorCode; - - public long getBadMsgId() { - return badMsgId; - } - - public int getBadMsqSeqno() { - return badMsqSeqno; - } - - public int getErrorCode() { - return errorCode; - } -} diff --git a/src/org/telegram/mtproto/tl/MTBadMessageNotification.java b/src/org/telegram/mtproto/tl/MTBadMessageNotification.java deleted file mode 100644 index b075970..0000000 --- a/src/org/telegram/mtproto/tl/MTBadMessageNotification.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:47 - */ -public class MTBadMessageNotification extends MTBadMessage { - - public static final int CLASS_ID = 0xa7eff811; - - public MTBadMessageNotification(long badMsgId, int badMsqSeqno, int errorCode) { - this.badMsgId = badMsgId; - this.badMsqSeqno = badMsqSeqno; - this.errorCode = errorCode; - } - - public MTBadMessageNotification() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(badMsgId, stream); - writeInt(badMsqSeqno, stream); - writeInt(errorCode, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - badMsgId = readLong(stream); - badMsqSeqno = readInt(stream); - errorCode = readInt(stream); - } - - @Override - public String toString() { - return "bad_msg_notification#a7eff811"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTBadServerSalt.java b/src/org/telegram/mtproto/tl/MTBadServerSalt.java deleted file mode 100644 index 0d874a6..0000000 --- a/src/org/telegram/mtproto/tl/MTBadServerSalt.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:45 - */ -public class MTBadServerSalt extends MTBadMessage { - - public static final int CLASS_ID = 0xedab447b; - - private long newSalt; - - public MTBadServerSalt(long messageId, int seqNo, int errorNo, long newSalt) { - this.badMsgId = messageId; - this.badMsqSeqno = seqNo; - this.errorCode = errorNo; - this.newSalt = newSalt; - } - - public MTBadServerSalt() { - - } - - public long getNewSalt() { - return newSalt; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(badMsgId, stream); - writeInt(badMsqSeqno, stream); - writeInt(errorCode, stream); - writeLong(newSalt, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - badMsgId = readLong(stream); - badMsqSeqno = readInt(stream); - errorCode = readInt(stream); - newSalt = readLong(stream); - } - - @Override - public String toString() { - return "bad_server_salt#edab447b"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTFutureSalt.java b/src/org/telegram/mtproto/tl/MTFutureSalt.java deleted file mode 100644 index e17f06d..0000000 --- a/src/org/telegram/mtproto/tl/MTFutureSalt.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 8:00 - */ -public class MTFutureSalt extends TLObject { - - public static final int CLASS_ID = 0x0949d9dc; - - private int validSince; - private int validUntil; - private long salt; - - public MTFutureSalt(int validSince, int validUntil, long salt) { - this.validSince = validSince; - this.validUntil = validUntil; - this.salt = salt; - } - - public MTFutureSalt() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public int getValidSince() { - return validSince; - } - - public int getValidUntil() { - return validUntil; - } - - public long getSalt() { - return salt; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeInt(validSince, stream); - writeInt(validUntil, stream); - writeLong(salt, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - validSince = readInt(stream); - validUntil = readInt(stream); - salt = readLong(stream); - } - - @Override - public String toString() { - return "future_salt#0949d9dc"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTFutureSalts.java b/src/org/telegram/mtproto/tl/MTFutureSalts.java deleted file mode 100644 index bcf6212..0000000 --- a/src/org/telegram/mtproto/tl/MTFutureSalts.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; -import org.telegram.tl.TLVector; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:58 - */ -public class MTFutureSalts extends TLObject { - public static final int CLASS_ID = 0xae500895; - - private long requestId; - private int now; - private TLVector salts = new TLVector(); - - public MTFutureSalts(long requestId, int now, TLVector salts) { - this.requestId = requestId; - this.now = now; - this.salts = salts; - } - - public MTFutureSalts() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public long getRequestId() { - return requestId; - } - - public int getNow() { - return now; - } - - public TLVector getSalts() { - return salts; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(requestId, stream); - writeInt(now, stream); - writeInt(salts.size(), stream); - for (MTFutureSalt salt : salts) { - salt.serializeBody(stream); - } - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - requestId = readLong(stream); - now = readInt(stream); - int count = readInt(stream); - salts.clear(); - for (int i = 0; i < count; i++) { - MTFutureSalt salt = new MTFutureSalt(); - salt.deserializeBody(stream, context); - salts.add(salt); - } - } - - @Override - public String toString() { - return "future_salts#ae500895"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTGetFutureSalts.java b/src/org/telegram/mtproto/tl/MTGetFutureSalts.java deleted file mode 100644 index e8f8489..0000000 --- a/src/org/telegram/mtproto/tl/MTGetFutureSalts.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.readInt; -import static org.telegram.tl.StreamingUtils.writeInt; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 7:56 - */ -public class MTGetFutureSalts extends TLObject { - - public static final int CLASS_ID = 0xb921bd04; - - private int num; - - public MTGetFutureSalts(int num) { - this.num = num; - } - - public MTGetFutureSalts() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeInt(num, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - num = readInt(stream); - } - - @Override - public String toString() { - return "get_future_salts#b921bd04"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTInvokeAfter.java b/src/org/telegram/mtproto/tl/MTInvokeAfter.java deleted file mode 100644 index a903611..0000000 --- a/src/org/telegram/mtproto/tl/MTInvokeAfter.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created by ex3ndr on 16.12.13. - */ -public class MTInvokeAfter extends TLObject { - public static final int CLASS_ID = 0xcb9f372d; - - private long dependMsgId; - - private byte[] request; - - public MTInvokeAfter(long dependMsgId, byte[] request) { - this.dependMsgId = dependMsgId; - this.request = request; - } - - public long getDependMsgId() { - return dependMsgId; - } - - public byte[] getRequest() { - return request; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(dependMsgId, stream); - writeByteArray(request, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - throw new UnsupportedOperationException("Unable to deserialize invokeAfterMsg#cb9f372d"); - } -} diff --git a/src/org/telegram/mtproto/tl/MTMessage.java b/src/org/telegram/mtproto/tl/MTMessage.java deleted file mode 100644 index 322d956..0000000 --- a/src/org/telegram/mtproto/tl/MTMessage.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.mtproto.util.BytesCache; -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 20:46 - */ -public class MTMessage extends TLObject { - private long messageId; - private int seqNo; - private byte[] content; - private int contentLen; - - public MTMessage(long messageId, int seqNo, byte[] content) { - this(messageId, seqNo, content, content.length); - } - - public MTMessage(long messageId, int seqNo, byte[] content, int contentLen) { - this.messageId = messageId; - this.seqNo = seqNo; - this.content = content; - this.contentLen = contentLen; - } - - public MTMessage() { - - } - - @Override - public int getClassId() { - return 0; - } - - public long getMessageId() { - return messageId; - } - - public void setMessageId(long messageId) { - this.messageId = messageId; - } - - public int getSeqNo() { - return seqNo; - } - - public void setSeqNo(int seqNo) { - this.seqNo = seqNo; - } - - public byte[] getContent() { - return content; - } - - public void setContent(byte[] content) { - this.content = content; - } - - public int getContentLen() { - return contentLen; - } - - public void setContentLen(int contentLen) { - this.contentLen = contentLen; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(messageId, stream); - writeInt(seqNo, stream); - writeInt(content.length, stream); - writeByteArray(content, 0, contentLen, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - messageId = readLong(stream); - seqNo = readInt(stream); - int size = readInt(stream); - content = BytesCache.getInstance().allocate(size); - readBytes(content, 0, size, stream); - } - - @Override - public String toString() { - return "message"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTMessageDetailedInfo.java b/src/org/telegram/mtproto/tl/MTMessageDetailedInfo.java deleted file mode 100644 index 847fc72..0000000 --- a/src/org/telegram/mtproto/tl/MTMessageDetailedInfo.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 8:40 - */ -public class MTMessageDetailedInfo extends TLObject { - public static final int CLASS_ID = 0x276d3ec6; - - private long msgId; - private long answerMsgId; - private int bytes; - private int state; - - public MTMessageDetailedInfo(long msgId, long answerMsgId, int bytes, int state) { - this.msgId = msgId; - this.answerMsgId = answerMsgId; - this.bytes = bytes; - this.state = state; - } - - public MTMessageDetailedInfo() { - - } - - public long getMsgId() { - return msgId; - } - - public long getAnswerMsgId() { - return answerMsgId; - } - - public int getBytes() { - return bytes; - } - - public int getState() { - return state; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(msgId, stream); - writeLong(answerMsgId, stream); - writeInt(bytes, stream); - writeInt(state, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - msgId = readLong(stream); - answerMsgId = readLong(stream); - bytes = readInt(stream); - state = readInt(stream); - } - - @Override - public String toString() { - return "msg_detailed_info#276d3ec6"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTMessagesContainer.java b/src/org/telegram/mtproto/tl/MTMessagesContainer.java deleted file mode 100644 index 3c820b4..0000000 --- a/src/org/telegram/mtproto/tl/MTMessagesContainer.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; -import org.telegram.tl.TLVector; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.TreeSet; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 20:53 - */ -public class MTMessagesContainer extends TLObject { - - public static final int CLASS_ID = 0x73f1f8dc; - - private TreeSet messages = new TreeSet(new Comparator() { - @Override - public int compare(MTMessage mtMessage, MTMessage mtMessage2) { - return (int) Math.signum(mtMessage.getMessageId() - mtMessage2.getMessageId()); - } - }); - - public MTMessagesContainer(MTMessage[] messages) { - Collections.addAll(this.messages, messages); - } - - public MTMessagesContainer() { - - } - - public TreeSet getMessages() { - return messages; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeInt(messages.size(), stream); - for (MTMessage message : messages) { - message.serializeBody(stream); - } - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - int size = readInt(stream); - messages.clear(); - for (int i = 0; i < size; i++) { - MTMessage message = new MTMessage(); - message.deserializeBody(stream, context); - messages.add(message); - } - } - - @Override - public String toString() { - return "msg_container#73f1f8dc"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTMsgsAck.java b/src/org/telegram/mtproto/tl/MTMsgsAck.java deleted file mode 100644 index 69a1693..0000000 --- a/src/org/telegram/mtproto/tl/MTMsgsAck.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLLongVector; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:30 - */ -public class MTMsgsAck extends TLObject { - - public static final int CLASS_ID = 0x62d6b459; - - private TLLongVector messages; - - public MTMsgsAck(TLLongVector messages) { - this.messages = messages; - } - - public MTMsgsAck() { - this.messages = new TLLongVector(); - } - - public MTMsgsAck(long[] msgIds) { - this.messages = new TLLongVector(); - for (long id : msgIds) { - this.messages.add(id); - } - } - - public MTMsgsAck(Long[] msgIds) { - this.messages = new TLLongVector(); - Collections.addAll(this.messages, msgIds); - } - - public TLLongVector getMessages() { - return messages; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeTLVector(messages, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - messages = readTLLongVector(stream, context); - } - - @Override - public String toString() { - return "msgs_ack#62d6b459"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTNeedResendMessage.java b/src/org/telegram/mtproto/tl/MTNeedResendMessage.java deleted file mode 100644 index f3e1656..0000000 --- a/src/org/telegram/mtproto/tl/MTNeedResendMessage.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLLongVector; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; - -import static org.telegram.tl.StreamingUtils.readTLLongVector; -import static org.telegram.tl.StreamingUtils.writeTLVector; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 8:50 - */ -public class MTNeedResendMessage extends TLObject { - - public static final int CLASS_ID = 0x7d861a08; - - private TLLongVector messages; - - public MTNeedResendMessage(TLLongVector messages) { - this.messages = messages; - } - - public MTNeedResendMessage() { - this.messages = new TLLongVector(); - } - - public MTNeedResendMessage(long[] msgIds) { - this.messages = new TLLongVector(); - for (long id : msgIds) { - this.messages.add(id); - } - } - - public MTNeedResendMessage(Long[] msgIds) { - this.messages = new TLLongVector(); - Collections.addAll(this.messages, msgIds); - } - - public TLLongVector getMessages() { - return messages; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeTLVector(messages, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - messages = readTLLongVector(stream, context); - } - - @Override - public String toString() { - return "msg_resend_req#7d861a08"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTNewMessageDetailedInfo.java b/src/org/telegram/mtproto/tl/MTNewMessageDetailedInfo.java deleted file mode 100644 index 07749e1..0000000 --- a/src/org/telegram/mtproto/tl/MTNewMessageDetailedInfo.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 07.11.13 - * Time: 8:37 - */ -public class MTNewMessageDetailedInfo extends TLObject { - - public static final int CLASS_ID = 0x809db6df; - - private long answerMsgId; - private int bytes; - private int status; - - public MTNewMessageDetailedInfo(long answerMsgId, int bytes, int status) { - this.answerMsgId = answerMsgId; - this.bytes = bytes; - this.status = status; - } - - public MTNewMessageDetailedInfo() { - - } - - public long getAnswerMsgId() { - return answerMsgId; - } - - public int getBytes() { - return bytes; - } - - public int getStatus() { - return status; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(answerMsgId, stream); - writeInt(bytes, stream); - writeInt(status, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - answerMsgId = readLong(stream); - bytes = readInt(stream); - status = readInt(stream); - } - - @Override - public String toString() { - return "msg_new_detailed_info#809db6df"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTNewSessionCreated.java b/src/org/telegram/mtproto/tl/MTNewSessionCreated.java deleted file mode 100644 index ce0a2df..0000000 --- a/src/org/telegram/mtproto/tl/MTNewSessionCreated.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:35 - */ -public class MTNewSessionCreated extends TLObject { - - public static final int CLASS_ID = 0x9ec20908; - - private long firstMsgId; - private byte[] uniqId; - private byte[] serverSalt; - - public MTNewSessionCreated(long firstMsgId, byte[] uniqId, byte[] serverSalt) { - this.firstMsgId = firstMsgId; - this.uniqId = uniqId; - this.serverSalt = serverSalt; - } - - public MTNewSessionCreated() { - - } - - public long getFirstMsgId() { - return firstMsgId; - } - - public byte[] getUniqId() { - return uniqId; - } - - public byte[] getServerSalt() { - return serverSalt; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(firstMsgId, stream); - writeByteArray(uniqId, stream); - writeByteArray(serverSalt, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - firstMsgId = readLong(stream); - uniqId = readBytes(8, stream); - serverSalt = readBytes(8, stream); - } - - @Override - public String toString() { - return "new_session_created#9ec20908"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTPing.java b/src/org/telegram/mtproto/tl/MTPing.java deleted file mode 100644 index 19d45a9..0000000 --- a/src/org/telegram/mtproto/tl/MTPing.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:22 - */ -public class MTPing extends TLObject { - public static final int CLASS_ID = 0x7abe77ec; - - private long pingId; - - public MTPing(long pingId) { - this.pingId = pingId; - } - - public MTPing() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(pingId, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - pingId = readLong(stream); - } - - @Override - public String toString() { - return "ping#7abe77ec"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTPingDelayDisconnect.java b/src/org/telegram/mtproto/tl/MTPingDelayDisconnect.java deleted file mode 100644 index 390d94b..0000000 --- a/src/org/telegram/mtproto/tl/MTPingDelayDisconnect.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:22 - */ -public class MTPingDelayDisconnect extends TLObject { - public static final int CLASS_ID = 0xf3427b8c; - - private long pingId; - private int disconnectDelay; - - public MTPingDelayDisconnect(long pingId, int disconnectDelay) { - this.pingId = pingId; - this.disconnectDelay = disconnectDelay; - } - - public MTPingDelayDisconnect() { - - } - - public long getPingId() { - return pingId; - } - - public int getDisconnectDelay() { - return disconnectDelay; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(pingId, stream); - writeInt(disconnectDelay, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - pingId = readLong(stream); - disconnectDelay = readInt(stream); - } - - @Override - public String toString() { - return "ping_delay_disconnect#f3427b8c"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTPong.java b/src/org/telegram/mtproto/tl/MTPong.java deleted file mode 100644 index f89ed64..0000000 --- a/src/org/telegram/mtproto/tl/MTPong.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:24 - */ -public class MTPong extends TLObject { - - public static final int CLASS_ID = 0x347773c5; - - private long messageId; - private long pingId; - - public MTPong(long messageId, long pingId) { - this.messageId = messageId; - this.pingId = pingId; - } - - public MTPong() { - } - - public long getMessageId() { - return messageId; - } - - public long getPingId() { - return pingId; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(messageId, stream); - writeLong(pingId, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - messageId = readLong(stream); - pingId = readLong(stream); - } - - @Override - public String toString() { - return "pong#347773c5"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTProtoContext.java b/src/org/telegram/mtproto/tl/MTProtoContext.java deleted file mode 100644 index b6f734f..0000000 --- a/src/org/telegram/mtproto/tl/MTProtoContext.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 8:22 - */ -public class MTProtoContext extends TLContext { - - // High performance singleton - private static class ContextHolder { - public static final MTProtoContext HOLDER_INSTANCE = new MTProtoContext(); - } - - public static MTProtoContext getInstance() { - return ContextHolder.HOLDER_INSTANCE; - } - - private MTProtoContext() { - - } - - @Override - protected void init() { - registerClass(MTPing.CLASS_ID, MTPing.class); - registerClass(MTPingDelayDisconnect.CLASS_ID, MTPingDelayDisconnect.class); - registerClass(MTPong.CLASS_ID, MTPong.class); - registerClass(MTMsgsAck.CLASS_ID, MTMsgsAck.class); - registerClass(MTNewSessionCreated.CLASS_ID, MTNewSessionCreated.class); - registerClass(MTBadMessageNotification.CLASS_ID, MTBadMessageNotification.class); - registerClass(MTBadServerSalt.CLASS_ID, MTBadServerSalt.class); - registerClass(MTNewMessageDetailedInfo.CLASS_ID, MTNewMessageDetailedInfo.class); - registerClass(MTMessageDetailedInfo.CLASS_ID, MTMessageDetailedInfo.class); - registerClass(MTNeedResendMessage.CLASS_ID, MTNeedResendMessage.class); - registerClass(MTMessagesContainer.CLASS_ID, MTMessagesContainer.class); - registerClass(MTRpcError.CLASS_ID, MTRpcError.class); - registerClass(MTRpcResult.CLASS_ID, MTRpcResult.class); - registerClass(MTGetFutureSalts.CLASS_ID, MTGetFutureSalts.class); - registerClass(MTFutureSalt.CLASS_ID, MTFutureSalt.class); - registerClass(MTFutureSalts.CLASS_ID, MTFutureSalts.class); - } -} diff --git a/src/org/telegram/mtproto/tl/MTRpcError.java b/src/org/telegram/mtproto/tl/MTRpcError.java deleted file mode 100644 index fc24bc9..0000000 --- a/src/org/telegram/mtproto/tl/MTRpcError.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 21:42 - */ -public class MTRpcError extends TLObject { - - private static final Pattern REGEXP_PATTERN = Pattern.compile("[A-Z_0-9]+"); - - public static final int CLASS_ID = 0x2144ca19; - - private int errorCode; - - private String message; - - public MTRpcError(int errorCode, String message) { - this.errorCode = errorCode; - this.message = message; - } - - public MTRpcError() { - - } - - public String getErrorTag() { - if (message == null) { - return "DEFAULT"; - } - Matcher matcher = REGEXP_PATTERN.matcher(message); - if (matcher.find()) { - return matcher.group(); - } - return "DEFAULT"; - } - - public int getErrorCode() { - return errorCode; - } - - public String getMessage() { - return message; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeInt(errorCode, stream); - writeTLString(message, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - errorCode = readInt(stream); - message = readTLString(stream); - } - - @Override - public String toString() { - return "rpc_error#2144ca19"; - } -} diff --git a/src/org/telegram/mtproto/tl/MTRpcResult.java b/src/org/telegram/mtproto/tl/MTRpcResult.java deleted file mode 100644 index 17078b6..0000000 --- a/src/org/telegram/mtproto/tl/MTRpcResult.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.telegram.mtproto.tl; - -import org.telegram.mtproto.util.BytesCache; -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 22:06 - */ -public class MTRpcResult extends TLObject { - - public static final int CLASS_ID = 0xf35c6d01; - - private long messageId; - private byte[] content; - private int contentLen; - - public MTRpcResult(long messageId, byte[] content, int contentLen) { - this.messageId = messageId; - this.content = content; - this.contentLen = contentLen; - } - - public MTRpcResult() { - - } - - public long getMessageId() { - return messageId; - } - - public byte[] getContent() { - return content; - } - - public int getContentLen() { - return contentLen; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeLong(messageId, stream); - writeByteArray(content, 0, contentLen, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - messageId = readLong(stream); - int contentSize = stream.available(); - content = BytesCache.getInstance().allocate(contentSize); - readBytes(content, 0, contentSize, stream); - } - - @Override - public String toString() { - return "rpc_result#f35c6d01"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ClientDhInner.java b/src/org/telegram/mtproto/tl/pq/ClientDhInner.java deleted file mode 100644 index ac25561..0000000 --- a/src/org/telegram/mtproto/tl/pq/ClientDhInner.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:32 - */ -public class ClientDhInner extends TLObject { - protected byte[] nonce; - protected byte[] serverNonce; - protected long retryId; - protected byte[] gb; - - public static final int CLASS_ID = 0x6643b654; - - public ClientDhInner(byte[] nonce, byte[] serverNonce, long retryId, byte[] gb) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.retryId = retryId; - this.gb = gb; - } - - public ClientDhInner() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public long getRetryId() { - return retryId; - } - - public byte[] getGb() { - return gb; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeLong(retryId, stream); - writeTLBytes(gb, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - retryId = readLong(stream); - gb = readTLBytes(stream); - } -} diff --git a/src/org/telegram/mtproto/tl/pq/DhGenFailure.java b/src/org/telegram/mtproto/tl/pq/DhGenFailure.java deleted file mode 100644 index 86df27b..0000000 --- a/src/org/telegram/mtproto/tl/pq/DhGenFailure.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:20 - */ -public class DhGenFailure extends DhGenResult { - public static final int CLASS_ID = 0xa69dae02; - - public DhGenFailure(byte[] nonce, byte[] serverNonce, byte[] newNonceHash) { - super(nonce, serverNonce, newNonceHash); - } - - public DhGenFailure() { - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public String toString() { - return "dh_gen_fail#a69dae02"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/DhGenOk.java b/src/org/telegram/mtproto/tl/pq/DhGenOk.java deleted file mode 100644 index 8f9c862..0000000 --- a/src/org/telegram/mtproto/tl/pq/DhGenOk.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:20 - */ -public class DhGenOk extends DhGenResult { - public static final int CLASS_ID = 0x3bcbf734; - - public DhGenOk(byte[] nonce, byte[] serverNonce, byte[] newNonceHash) { - super(nonce, serverNonce, newNonceHash); - } - - public DhGenOk() { - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public String toString() { - return "dh_gen_ok#3bcbf734"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/DhGenResult.java b/src/org/telegram/mtproto/tl/pq/DhGenResult.java deleted file mode 100644 index d16e90e..0000000 --- a/src/org/telegram/mtproto/tl/pq/DhGenResult.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.readBytes; -import static org.telegram.tl.StreamingUtils.writeByteArray; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:19 - */ -public abstract class DhGenResult extends TLObject { - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] newNonceHash; - - protected DhGenResult(byte[] nonce, byte[] serverNonce, byte[] newNonceHash) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.newNonceHash = newNonceHash; - } - - - public DhGenResult() { - - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public byte[] getNewNonceHash() { - return newNonceHash; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeByteArray(newNonceHash, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - newNonceHash = readBytes(16, stream); - } -} diff --git a/src/org/telegram/mtproto/tl/pq/DhGenRetry.java b/src/org/telegram/mtproto/tl/pq/DhGenRetry.java deleted file mode 100644 index 8b30510..0000000 --- a/src/org/telegram/mtproto/tl/pq/DhGenRetry.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:20 - */ -public class DhGenRetry extends DhGenResult { - public static final int CLASS_ID = 0x46dc1fb9; - - public DhGenRetry(byte[] nonce, byte[] serverNonce, byte[] newNonceHash) { - super(nonce, serverNonce, newNonceHash); - } - - public DhGenRetry() { - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public String toString() { - return "dh_gen_retry#46dc1fb9"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/PQInner.java b/src/org/telegram/mtproto/tl/pq/PQInner.java deleted file mode 100644 index f1120ba..0000000 --- a/src/org/telegram/mtproto/tl/pq/PQInner.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:20 - */ -public class PQInner extends TLObject { - public static final int CLASS_ID = 0x83c95aec; - - protected byte[] pq; - protected byte[] p; - protected byte[] q; - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] newNonce; - - public PQInner(byte[] pq, byte[] p, byte[] q, byte[] nonce, byte[] serverNonce, byte[] newNonce) { - this.pq = pq; - this.p = p; - this.q = q; - this.nonce = nonce; - this.serverNonce = serverNonce; - this.newNonce = newNonce; - } - - public PQInner() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getPq() { - return pq; - } - - public byte[] getP() { - return p; - } - - public byte[] getQ() { - return q; - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public byte[] getNewNonce() { - return newNonce; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeTLBytes(pq, stream); - writeTLBytes(p, stream); - writeTLBytes(q, stream); - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeByteArray(newNonce, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - pq = readTLBytes(stream); - p = readTLBytes(stream); - q = readTLBytes(stream); - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - newNonce = readBytes(32, stream); - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ReqDhParams.java b/src/org/telegram/mtproto/tl/pq/ReqDhParams.java deleted file mode 100644 index b125c0f..0000000 --- a/src/org/telegram/mtproto/tl/pq/ReqDhParams.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.DeserializeException; -import org.telegram.tl.TLContext; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:26 - */ -public class ReqDhParams extends TLMethod { - - public static final int CLASS_ID = 0xd712e4be; - - @Override - public ServerDhParams deserializeResponse(InputStream stream, TLContext context) throws IOException { - TLObject response = context.deserializeMessage(stream); - - if (response == null) { - throw new DeserializeException("Unable to deserialize response"); - } - if (!(response instanceof ServerDhParams)) { - throw new DeserializeException("Response has incorrect type"); - } - - return (ServerDhParams) response; - } - - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] p; - protected byte[] q; - protected long fingerPrint; - protected byte[] encryptedData; - - public ReqDhParams(byte[] nonce, byte[] serverNonce, byte[] p, byte[] q, long fingerPrint, byte[] encryptedData) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.p = p; - this.q = q; - this.fingerPrint = fingerPrint; - this.encryptedData = encryptedData; - } - - public ReqDhParams() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public byte[] getP() { - return p; - } - - public byte[] getQ() { - return q; - } - - public long getFingerPrint() { - return fingerPrint; - } - - public byte[] getEncryptedData() { - return encryptedData; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeTLBytes(p, stream); - writeTLBytes(q, stream); - writeLong(fingerPrint, stream); - writeTLBytes(encryptedData, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - p = readTLBytes(stream); - q = readTLBytes(stream); - fingerPrint = readLong(stream); - encryptedData = readTLBytes(stream); - } - - @Override - public String toString() { - return "req_DH_params#d712e4be"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ReqPQ.java b/src/org/telegram/mtproto/tl/pq/ReqPQ.java deleted file mode 100644 index 683db08..0000000 --- a/src/org/telegram/mtproto/tl/pq/ReqPQ.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import static org.telegram.tl.StreamingUtils.*; - -import org.telegram.tl.DeserializeException; -import org.telegram.tl.TLContext; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 4:17 - */ -public class ReqPQ extends TLMethod { - - public static final int CLASS_ID = 0x60469778; - - protected byte[] nonce; - - public ReqPQ(byte[] nonce) { - if (nonce == null || nonce.length != 16) { - throw new IllegalArgumentException("nonce might be not null and 16 bytes length"); - } - this.nonce = nonce; - } - - public ReqPQ() { - - } - - @Override - public ResPQ deserializeResponse(InputStream stream, TLContext context) throws IOException { - TLObject response = context.deserializeMessage(stream); - if (response == null) { - throw new DeserializeException("Unable to deserialize response"); - } - if (!(response instanceof ResPQ)) { - throw new DeserializeException("Response has incorrect type"); - } - - return (ResPQ) response; - } - - public byte[] getNonce() { - return nonce; - } - - public void setNonce(byte[] nonce) { - if (nonce == null || nonce.length != 16) { - throw new IllegalArgumentException("nonce might be not null and 16 bytes length"); - } - this.nonce = nonce; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - } - - @Override - public String toString() { - return "req_pq#60469778"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ReqSetDhClientParams.java b/src/org/telegram/mtproto/tl/pq/ReqSetDhClientParams.java deleted file mode 100644 index 4e640fc..0000000 --- a/src/org/telegram/mtproto/tl/pq/ReqSetDhClientParams.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.DeserializeException; -import org.telegram.tl.TLContext; -import org.telegram.tl.TLMethod; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 7:31 - */ -public class ReqSetDhClientParams extends TLMethod { - - public static final int CLASS_ID = 0xf5045f1f; - - @Override - public DhGenResult deserializeResponse(InputStream stream, TLContext context) throws IOException { - TLObject response = context.deserializeMessage(stream); - if (response == null) { - throw new DeserializeException("Unable to deserialize response"); - } - if (!(response instanceof DhGenResult)) { - throw new DeserializeException("Response has incorrect type"); - } - return (DhGenResult) response; - } - - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] encrypted; - - public ReqSetDhClientParams(byte[] nonce, byte[] serverNonce, byte[] encrypted) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.encrypted = encrypted; - } - - public ReqSetDhClientParams() { - - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public byte[] getEncrypted() { - return encrypted; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeTLBytes(encrypted, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - encrypted = readTLBytes(stream); - } - - @Override - public String toString() { - return "set_client_DH_params#f5045f1f"; - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/tl/pq/ResPQ.java b/src/org/telegram/mtproto/tl/pq/ResPQ.java deleted file mode 100644 index def1202..0000000 --- a/src/org/telegram/mtproto/tl/pq/ResPQ.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLLongVector; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 5:31 - */ -public class ResPQ extends TLObject { - - public static final int CLASS_ID = 0x05162463; - - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] pq; - protected TLLongVector fingerprints; - - public ResPQ(byte[] nonce, byte[] serverNonce, byte[] pq, TLLongVector fingerprints) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.pq = pq; - this.fingerprints = fingerprints; - } - - public ResPQ() { - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getNonce() { - return nonce; - } - - public void setNonce(byte[] nonce) { - this.nonce = nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public void setServerNonce(byte[] serverNonce) { - this.serverNonce = serverNonce; - } - - public byte[] getPq() { - return pq; - } - - public void setPq(byte[] pq) { - this.pq = pq; - } - - public TLLongVector getFingerprints() { - return fingerprints; - } - - public void setFingerprints(TLLongVector fingerprints) { - this.fingerprints = fingerprints; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeTLBytes(pq, stream); - writeTLVector(fingerprints, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - pq = readTLBytes(stream); - fingerprints = readTLLongVector(stream, context); - } - - @Override - public String toString() { - return "resPQ#05162463"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ServerDhFailure.java b/src/org/telegram/mtproto/tl/pq/ServerDhFailure.java deleted file mode 100644 index f9405af..0000000 --- a/src/org/telegram/mtproto/tl/pq/ServerDhFailure.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:31 - */ -public class ServerDhFailure extends ServerDhParams { - - public static final int CLASS_ID = 0x79cb045d; - - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] newNonceHash; - - public ServerDhFailure(byte[] nonce, byte[] serverNonce, byte[] newNonceHash) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.newNonceHash = newNonceHash; - } - - public ServerDhFailure() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getNonce() { - return nonce; - } - - public void setNonce(byte[] nonce) { - this.nonce = nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public void setServerNonce(byte[] serverNonce) { - this.serverNonce = serverNonce; - } - - public byte[] getNewNonceHash() { - return newNonceHash; - } - - public void setNewNonceHash(byte[] newNonceHash) { - this.newNonceHash = newNonceHash; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeByteArray(newNonceHash, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - newNonceHash = readBytes(16, stream); - } - - @Override - public String toString() { - return "server_DH_params_fail#79cb045d"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ServerDhInner.java b/src/org/telegram/mtproto/tl/pq/ServerDhInner.java deleted file mode 100644 index 4ba88ae..0000000 --- a/src/org/telegram/mtproto/tl/pq/ServerDhInner.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; -import org.telegram.tl.TLObject; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:56 - */ -public class ServerDhInner extends TLObject { - public static final int CLASS_ID = 0xb5890dba; - - protected byte[] nonce; - protected byte[] serverNonce; - protected int g; - protected byte[] dhPrime; - protected byte[] g_a; - protected int serverTime; - - public ServerDhInner(byte[] nonce, byte[] serverNonce, int g, byte[] dhPrime, byte[] g_a, int serverTime) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.g = g; - this.dhPrime = dhPrime; - this.g_a = g_a; - this.serverTime = serverTime; - } - - public ServerDhInner() { - - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public int getG() { - return g; - } - - public byte[] getDhPrime() { - return dhPrime; - } - - public byte[] getG_a() { - return g_a; - } - - public int getServerTime() { - return serverTime; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeInt(g, stream); - writeTLBytes(dhPrime, stream); - writeTLBytes(g_a, stream); - writeInt(serverTime, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - g = readInt(stream); - dhPrime = readTLBytes(stream); - g_a = readTLBytes(stream); - serverTime = readInt(stream); - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ServerDhOk.java b/src/org/telegram/mtproto/tl/pq/ServerDhOk.java deleted file mode 100644 index 2587736..0000000 --- a/src/org/telegram/mtproto/tl/pq/ServerDhOk.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import static org.telegram.tl.StreamingUtils.*; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:29 - */ -public class ServerDhOk extends ServerDhParams { - - public static final int CLASS_ID = 0xd0e8075c; - - protected byte[] nonce; - protected byte[] serverNonce; - protected byte[] encryptedAnswer; - - public ServerDhOk(byte[] nonce, byte[] serverNonce, byte[] encryptedAnswer) { - this.nonce = nonce; - this.serverNonce = serverNonce; - this.encryptedAnswer = encryptedAnswer; - } - - public ServerDhOk() { - - } - - public byte[] getNonce() { - return nonce; - } - - public byte[] getServerNonce() { - return serverNonce; - } - - public byte[] getEncryptedAnswer() { - return encryptedAnswer; - } - - @Override - public int getClassId() { - return CLASS_ID; - } - - @Override - public void serializeBody(OutputStream stream) throws IOException { - writeByteArray(nonce, stream); - writeByteArray(serverNonce, stream); - writeTLBytes(encryptedAnswer, stream); - } - - @Override - public void deserializeBody(InputStream stream, TLContext context) throws IOException { - nonce = readBytes(16, stream); - serverNonce = readBytes(16, stream); - encryptedAnswer = readTLBytes(stream); - } - - @Override - public String toString() { - return "server_DH_params_ok#d0e8075c"; - } -} diff --git a/src/org/telegram/mtproto/tl/pq/ServerDhParams.java b/src/org/telegram/mtproto/tl/pq/ServerDhParams.java deleted file mode 100644 index 9f5e984..0000000 --- a/src/org/telegram/mtproto/tl/pq/ServerDhParams.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLObject; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 6:27 - */ -public abstract class ServerDhParams extends TLObject { -} diff --git a/src/org/telegram/mtproto/tl/pq/TLInitContext.java b/src/org/telegram/mtproto/tl/pq/TLInitContext.java deleted file mode 100644 index 2d6c2f2..0000000 --- a/src/org/telegram/mtproto/tl/pq/TLInitContext.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.telegram.mtproto.tl.pq; - -import org.telegram.tl.TLContext; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 5:29 - */ -public class TLInitContext extends TLContext { - @Override - protected void init() { - registerClass(ReqPQ.CLASS_ID, ReqPQ.class); - registerClass(ResPQ.CLASS_ID, ResPQ.class); - registerClass(ReqDhParams.CLASS_ID, ReqDhParams.class); - registerClass(ServerDhOk.CLASS_ID, ServerDhOk.class); - registerClass(ServerDhFailure.CLASS_ID, ServerDhFailure.class); - registerClass(ServerDhInner.CLASS_ID, ServerDhInner.class); - registerClass(DhGenOk.CLASS_ID, DhGenOk.class); - registerClass(DhGenFailure.CLASS_ID, DhGenFailure.class); - registerClass(DhGenRetry.CLASS_ID, DhGenRetry.class); - registerClass(ReqSetDhClientParams.CLASS_ID, ReqSetDhClientParams.class); - registerClass(ClientDhInner.CLASS_ID, ClientDhInner.class); - } -} diff --git a/src/org/telegram/mtproto/transport/ConnectionType.java b/src/org/telegram/mtproto/transport/ConnectionType.java deleted file mode 100644 index 2d6ad92..0000000 --- a/src/org/telegram/mtproto/transport/ConnectionType.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.telegram.mtproto.transport; - -/** - * Created by ex3ndr on 26.11.13. - */ -public class ConnectionType { - public static final int TYPE_TCP = 0; - - private int id; - private String host; - private int port; - private int connectionType; - - public ConnectionType(int id, String host, int port, int connectionType) { - this.id = id; - this.host = host; - this.port = port; - this.connectionType = connectionType; - } - - public int getId() { - return id; - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public int getConnectionType() { - return connectionType; - } -} diff --git a/src/org/telegram/mtproto/transport/PlainTcpConnection.java b/src/org/telegram/mtproto/transport/PlainTcpConnection.java deleted file mode 100644 index 5963a1b..0000000 --- a/src/org/telegram/mtproto/transport/PlainTcpConnection.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.telegram.mtproto.transport; - -import org.telegram.mtproto.log.Logger; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Socket; - -import static org.telegram.tl.StreamingUtils.readBytes; -import static org.telegram.tl.StreamingUtils.writeByte; -import static org.telegram.tl.StreamingUtils.writeByteArray; - -/** - * Created with IntelliJ IDEA. - * User: ex3ndr - * Date: 03.11.13 - * Time: 5:04 - */ -public class PlainTcpConnection { - private static final String TAG = "PlainTcpConnection"; - - private static final int CONNECTION_TIMEOUT = 5 * 1000; - - private Socket socket; - - private boolean isBroken; - - public PlainTcpConnection(String ip, int port) throws IOException { - this.socket = new Socket(); - this.socket.connect(new InetSocketAddress(ip, port), CONNECTION_TIMEOUT); - this.socket.setKeepAlive(true); - this.socket.setTcpNoDelay(true); - this.socket.getOutputStream().write(0xef); - this.isBroken = false; - } - - public Socket getSocket() { - return socket; - } - - private byte[] readMessage() throws IOException { - InputStream stream = socket.getInputStream(); - int headerLen = readByte(stream); - - if (headerLen == 0x7F) { - headerLen = readByte(stream) + (readByte(stream) << 8) + (readByte(stream) << 16); - } - int len = headerLen * 4; - return readBytes(len, stream); - } - - private void writeMessage(byte[] request) throws IOException { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - if (request.length / 4 >= 0x7F) { - int len = request.length / 4; - writeByte(0x7F, stream); - writeByte(len & 0xFF, stream); - writeByte((len >> 8) & 0xFF, stream); - writeByte((len >> 16) & 0xFF, stream); - } else { - writeByte(request.length / 4, stream); - } - writeByteArray(request, stream); - byte[] pkg = stream.toByteArray(); - socket.getOutputStream().write(pkg, 0, pkg.length); - socket.getOutputStream().flush(); - } - - public byte[] executeMethod(byte[] request) throws IOException { - writeMessage(request); - return readMessage(); - } - - public void destroy() { - try { - socket.close(); - } catch (IOException e) { - Logger.e(TAG, e); - } - } - - private int readByte(InputStream stream) throws IOException { - int res = stream.read(); - if (res < 0) { - throw new IOException(); - } - return res; - } -} diff --git a/src/org/telegram/mtproto/transport/TcpContext.java b/src/org/telegram/mtproto/transport/TcpContext.java deleted file mode 100644 index 165d2b3..0000000 --- a/src/org/telegram/mtproto/transport/TcpContext.java +++ /dev/null @@ -1,652 +0,0 @@ -package org.telegram.mtproto.transport; - -import org.telegram.mtproto.MTProto; -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.util.BytesCache; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.CRC32; - -/** - * Author: Korshakov Stepan - * Created: 13.08.13 14:56 - */ -public class TcpContext { - private static final boolean LOG_OPERATIONS = false; - private static final int MAX_PACKED_SIZE = 1024 * 1024 * 1024;//1 MB - - private class Package { - public Package() { - - } - - private Package(byte[] data, boolean useFastConfirm) { - this.data = data; - this.useFastConfirm = useFastConfirm; - } - - public byte[] data; - public boolean useFastConfirm; - } - - private final String TAG; - - private static final AtomicInteger contextLastId = new AtomicInteger(1); - - private static final int CONNECTION_TIMEOUT = 5 * 1000; - - private static final int READ_TIMEOUT = 1000; - - private static final int READ_DIE_TIMEOUT = 5 * 1000; // 5 sec - - private final String ip; - private final int port; - private final boolean useChecksum; - - private int sentPackets; - private int receivedPackets; - - private boolean isClosed; - private boolean isBroken; - - private Socket socket; - - private ReaderThread readerThread; - - private WriterThread writerThread; - - private DieThread dieThread; - - private TcpContextCallback callback; - - private final int contextId; - - private long lastWriteEvent = 0; - - public TcpContext(MTProto proto, String ip, int port, boolean checksum, TcpContextCallback callback) throws IOException { - try { - this.contextId = contextLastId.incrementAndGet(); - this.TAG = "MTProto#" + proto.getInstanceIndex() + "#Transport" + contextId; - this.ip = ip; - this.port = port; - this.useChecksum = checksum; - this.socket = new Socket(); - this.socket.connect(new InetSocketAddress(ip, port), CONNECTION_TIMEOUT); - this.socket.setKeepAlive(true); - this.socket.setTcpNoDelay(true); - if (!useChecksum) { - socket.getOutputStream().write(0xef); - } - this.isClosed = false; - this.isBroken = false; - this.callback = callback; - this.readerThread = new ReaderThread(); - this.writerThread = new WriterThread(); - this.dieThread = new DieThread(); - this.readerThread.start(); - this.writerThread.start(); - this.dieThread.start(); - } catch (IOException e) { - throw e; - } catch (Throwable t) { - throw new IOException(); - } - } - - public int getContextId() { - return contextId; - } - - public String getIp() { - return ip; - } - - public int getPort() { - return port; - } - - public boolean isUseChecksum() { - return useChecksum; - } - - public int getSentPackets() { - return sentPackets; - } - - public int getReceivedPackets() { - return receivedPackets; - } - - public boolean isClosed() { - return isClosed; - } - - public boolean isBroken() { - return isBroken; - } - - public void postMessage(byte[] data, boolean useFastConfirm) { - writerThread.pushPackage(new Package(data, useFastConfirm)); - } - - public synchronized void close() { - if (!isClosed) { - Logger.w(TAG, "Manual context closing"); - isClosed = true; - isBroken = false; - try { - readerThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - try { - writerThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - - try { - dieThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - } - - private synchronized void onMessage(byte[] data, int len) { - if (isClosed) { - return; - } - - callback.onRawMessage(data, 0, len, this); - } - - private synchronized void onError(int errorCode) { - if (isClosed) { - return; - } - - callback.onError(errorCode, this); - } - - private synchronized void breakContext() { - if (!isClosed) { - Logger.w(TAG, "Breaking context"); - isClosed = true; - isBroken = true; - try { - readerThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - try { - writerThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - - try { - dieThread.interrupt(); - } catch (Exception e) { - Logger.e(TAG, e); - } - } - - callback.onChannelBroken(this); - } - - private class ReaderThread extends Thread { - private ReaderThread() { - setPriority(Thread.MIN_PRIORITY); - setName(TAG + "#Reader" + hashCode()); - } - - @Override - public void run() { - try { - while (!isClosed && !isInterrupted()) { - if (LOG_OPERATIONS) { - Logger.d(TAG, "Reader Iteration"); - } - try { - if (socket.isClosed()) { - breakContext(); - return; - } - if (!socket.isConnected()) { - breakContext(); - return; - } - - InputStream stream = socket.getInputStream(); - - byte[] pkg = null; - int pkgLen; - if (useChecksum) { - int length = readInt(stream); - if (arrayEq(intToBytes(length), "HTTP".getBytes())) { - Logger.d(TAG, "Received HTTP package"); - breakContext(); - return; - } - Logger.d(TAG, "Start reading message: " + length); - if (length <= 0) { - breakContext(); - return; - } - - if ((length >> 31) != 0) { - Logger.d(TAG, "fast confirm: " + Integer.toHexString(length)); - callback.onFastConfirm(length); - continue; - } - - if (length >= MAX_PACKED_SIZE) { - Logger.d(TAG, "Too big package"); - breakContext(); - return; - } - - int packetIndex = readInt(stream); - if (length == 4) { - onError(packetIndex); - Logger.d(TAG, "Received error: " + packetIndex); - breakContext(); - return; - } - pkgLen = length - 12; - pkg = readBytes(pkgLen, stream); - int readCrc = readInt(stream); - CRC32 crc32 = new CRC32(); - crc32.update(intToBytes(length)); - crc32.update(intToBytes(packetIndex)); - crc32.update(pkg, 0, pkgLen); - if (readCrc != (int) crc32.getValue()) { - Logger.d(TAG, "Incorrect CRC"); - breakContext(); - return; - } - - if (receivedPackets != packetIndex) { - Logger.d(TAG, "Incorrect packet index"); - breakContext(); - return; - } - - receivedPackets++; - } else { - int length = readByte(stream); - - Logger.d(TAG, "Start reading message: pre"); - - if (length >> 7 != 0) { - length = (length << 24) + (readByte(stream) << 16) + (readByte(stream) << 8) + (readByte(stream) << 0); - Logger.d(TAG, "fast confirm: " + Integer.toHexString(length)); - callback.onFastConfirm(length); - continue; - } else { - if (length == 0x7F) { - length = readByte(stream) + (readByte(stream) << 8) + (readByte(stream) << 16); - } - int len = length * 4; - - Logger.d(TAG, "Start reading message: " + len); - - if (length == 4) { - int error = readInt(stream); - onError(error); - Logger.d(TAG, "Received error: " + error); - breakContext(); - return; - } - - if (length >= MAX_PACKED_SIZE) { - Logger.d(TAG, "Too big package"); - breakContext(); - return; - } - - pkgLen = len; - pkg = readBytes(len, READ_TIMEOUT, stream); - } - } - try { - onMessage(pkg, pkgLen); - } catch (Throwable t) { - Logger.e(TAG, t); - Logger.d(TAG, "Message processing error"); - breakContext(); - return; - } finally { - if (pkg != null) { - BytesCache.getInstance().put(pkg); - } - } - } catch (IOException e) { - Logger.e(TAG, e); - breakContext(); - return; - } - } - } catch (Throwable e) { - Logger.e(TAG, e); - breakContext(); - } - } - } - - private class WriterThread extends Thread { - private final ConcurrentLinkedQueue packages = new ConcurrentLinkedQueue(); - - public WriterThread() { - setPriority(Thread.MIN_PRIORITY); - setName(TAG + "#Writer" + hashCode()); - } - - public void pushPackage(Package p) { - packages.add(p); - synchronized (packages) { - packages.notifyAll(); - } - } - - @Override - public void run() { - while (!isBroken) { - if (LOG_OPERATIONS) { - Logger.d(TAG, "Writer Iteration"); - } - Package p; - synchronized (packages) { - p = packages.poll(); - if (p == null) { - try { - packages.wait(); - } catch (InterruptedException e) { - // e.printStackTrace(); - return; - } - p = packages.poll(); - } - } - if (p == null) { - if (isBroken) { - return; - } else { - continue; - } - } - - if (LOG_OPERATIONS) { - Logger.d(TAG, "Writing data"); - } - - try { - - byte[] data = p.data; - boolean useConfimFlag = p.useFastConfirm; - - if (useChecksum) { - OutputStream stream = socket.getOutputStream(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - int len = data.length + 12; - if (useConfimFlag) { - len |= (1 << 31); - } - writeInt(len, outputStream); - writeInt(sentPackets, outputStream); - writeByteArray(data, outputStream); - CRC32 crc32 = new CRC32(); - crc32.update(intToBytes(len)); - crc32.update(intToBytes(sentPackets)); - crc32.update(data); - writeInt((int) (crc32.getValue() & 0xFFFFFFFF), outputStream); - writeByteArray(outputStream.toByteArray(), stream); - onWrite(); - stream.flush(); - } else { - OutputStream stream = socket.getOutputStream(); - if (useConfimFlag) { - if (data.length / 4 >= 0x7F) { - int len = data.length / 4; - writeByte(0xFF, stream); - writeByte(len & 0xFF, stream); - writeByte((len >> 8) & 0xFF, stream); - writeByte((len >> 16) & 0xFF, stream); - } else { - writeByte((data.length / 4) | (1 << 7), stream); - } - } else { - if (data.length / 4 >= 0x7F) { - int len = data.length / 4; - writeByte(0x7F, stream); - writeByte(len & 0xFF, stream); - writeByte((len >> 8) & 0xFF, stream); - writeByte((len >> 16) & 0xFF, stream); - } else { - writeByte(data.length / 4, stream); - } - } - writeByteArray(data, stream); - onWrite(); - stream.flush(); - } - sentPackets++; - } catch (Exception e) { - Logger.e(TAG, e); - breakContext(); - } - - if (LOG_OPERATIONS) { - Logger.d(TAG, "End writing data"); - } - } - } - } - - private void onWrite() { - lastWriteEvent = System.nanoTime(); - notifyDieThread(); - } - - private void onRead() { - lastWriteEvent = 0; - notifyDieThread(); - } - - private void notifyDieThread() { -// synchronized (dieThread) { -// dieThread.notifyAll(); -// } - } - - private class DieThread extends Thread { - public DieThread() { - setPriority(Thread.MIN_PRIORITY); - setName(TAG + "#DieThread" + hashCode()); - } - - @Override - public void run() { - while (!isBroken) { - if (Logger.LOG_THREADS) { - Logger.d(TAG, "DieThread iteration"); - } - if (lastWriteEvent != 0) { - long delta = (System.nanoTime() - lastWriteEvent) / (1000 * 1000); - if (delta >= READ_DIE_TIMEOUT) { - Logger.d(TAG, "Dies by timeout"); - breakContext(); - } else { - try { - int waitDelta = (int) (READ_DIE_TIMEOUT - delta); - // Logger.d(TAG, "DieThread wait: " + waitDelta); - sleep(Math.max(waitDelta, 1000)); - // Logger.d(TAG, "DieThread start wait end"); - } catch (InterruptedException e) { - Logger.d(TAG, "DieThread exit"); - return; - } - } - } else { - try { - // Logger.d(TAG, "DieThread start common wait"); - sleep(READ_DIE_TIMEOUT); - // Logger.d(TAG, "DieThread end common wait"); - } catch (InterruptedException e) { - Logger.d(TAG, "DieThread exit"); - return; - } - } - } - Logger.d(TAG, "DieThread exit"); - } - } - - - private void writeByteArray(byte[] data, OutputStream stream) throws IOException { - stream.write(data); - } - - private byte[] intToBytes(int value) { - return new byte[]{ - (byte) (value & 0xFF), - (byte) ((value >> 8) & 0xFF), - (byte) ((value >> 16) & 0xFF), - (byte) ((value >> 24) & 0xFF)}; - } - - private void writeInt(int value, OutputStream stream) throws IOException { - stream.write((byte) (value & 0xFF)); - stream.write((byte) ((value >> 8) & 0xFF)); - stream.write((byte) ((value >> 16) & 0xFF)); - stream.write((byte) ((value >> 24) & 0xFF)); - } - - private void writeByte(int v, OutputStream stream) throws IOException { - stream.write(v); - } - - private void writeByte(byte v, OutputStream stream) throws IOException { - stream.write(v); - } - - private byte[] readBytes(int count, int timeout, InputStream stream) throws IOException { - byte[] res = BytesCache.getInstance().allocate(count); - int offset = 0; - long start = System.nanoTime(); - while (offset < count) { - int readed = stream.read(res, offset, count - offset); - Thread.yield(); - if (readed > 0) { - offset += readed; - onRead(); - } else if (readed < 0) { - throw new IOException(); - } else { - if (System.nanoTime() - start > timeout * 1000000L) { - throw new IOException(); - } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Logger.e(TAG, e); - throw new IOException(); - } - } - } - return res; - } - - private byte[] readBytes(int count, InputStream stream) throws IOException { - byte[] res = BytesCache.getInstance().allocate(count); - int offset = 0; - while (offset < count) { - int readed = stream.read(res, offset, count - offset); - Thread.yield(); - if (readed > 0) { - offset += readed; - onRead(); - } else if (readed < 0) { - throw new IOException(); - } else { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Logger.e(TAG, e); - throw new IOException(); - } - } - } - return res; - } - - private int readInt(InputStream stream) throws IOException { - int a = stream.read(); - if (a < 0) { - throw new IOException(); - } - onRead(); - - int b = stream.read(); - if (b < 0) { - throw new IOException(); - } - onRead(); - - int c = stream.read(); - if (c < 0) { - throw new IOException(); - } - onRead(); - - int d = stream.read(); - if (d < 0) { - throw new IOException(); - } - onRead(); - - return a + (b << 8) + (c << 16) + (d << 24); - } - - private int readInt(byte[] src) { - return readInt(src, 0); - } - - private int readInt(byte[] src, int offset) { - int a = src[offset + 0] & 0xFF; - int b = src[offset + 1] & 0xFF; - int c = src[offset + 2] & 0xFF; - int d = src[offset + 3] & 0xFF; - - return a + (b << 8) + (c << 16) + (d << 24); - } - - private int readByte(InputStream stream) throws IOException { - int res = stream.read(); - if (res < 0) { - throw new IOException(); - } - onRead(); - return res; - } - - private boolean arrayEq(byte[] a, byte[] b) { - if (a.length != b.length) { - return false; - } - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) - return false; - } - return true; - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/transport/TcpContextCallback.java b/src/org/telegram/mtproto/transport/TcpContextCallback.java deleted file mode 100644 index 2ebb017..0000000 --- a/src/org/telegram/mtproto/transport/TcpContextCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.telegram.mtproto.transport; - -/** - * Author: Korshakov Stepan - * Created: 13.08.13 15:35 - */ -public interface TcpContextCallback { - public void onRawMessage(byte[] data, int offset, int len, TcpContext context); - - public void onError(int errorCode, TcpContext context); - - public void onChannelBroken(TcpContext context); - - public void onFastConfirm(int hash); -} diff --git a/src/org/telegram/mtproto/transport/TransportPool.java b/src/org/telegram/mtproto/transport/TransportPool.java deleted file mode 100644 index d44f289..0000000 --- a/src/org/telegram/mtproto/transport/TransportPool.java +++ /dev/null @@ -1,264 +0,0 @@ -package org.telegram.mtproto.transport; - -import org.telegram.mtproto.MTProto; -import org.telegram.mtproto.schedule.Scheduller; -import org.telegram.mtproto.schedule.SchedullerListener; -import org.telegram.mtproto.state.AbsMTProtoState; -import org.telegram.mtproto.time.TimeOverlord; -import org.telegram.mtproto.tl.MTMessage; -import org.telegram.mtproto.tl.MTMessagesContainer; -import org.telegram.mtproto.tl.MTProtoContext; -import org.telegram.mtproto.util.BytesCache; -import org.telegram.tl.StreamingUtils; -import org.telegram.tl.TLObject; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import static org.telegram.mtproto.secure.CryptoUtils.*; -import static org.telegram.mtproto.secure.CryptoUtils.align; -import static org.telegram.tl.StreamingUtils.*; -import static org.telegram.tl.StreamingUtils.writeByteArray; - -/** - * Created by ex3ndr on 03.04.14. - */ -public abstract class TransportPool implements SchedullerListener { - - public static final int MODE_DEFAULT = 0; - public static final int MODE_LOWMODE = 1; - - protected MTProto proto; - protected AbsMTProtoState state; - protected Scheduller scheduller; - protected int mode = MODE_DEFAULT; - protected MTProtoContext context; - private TransportPoolCallback transportPoolCallback; - - private byte[] authKey; - private byte[] authKeyId; - private byte[] session; - - protected boolean isClosed = false; - - public TransportPool(MTProto proto, TransportPoolCallback transportPoolCallback) { - this.proto = proto; - this.scheduller = proto.getScheduller(); - this.scheduller.addListener(this); - this.transportPoolCallback = transportPoolCallback; - this.context = MTProtoContext.getInstance(); - this.authKey = proto.getAuthKey(); - this.authKeyId = proto.getAuthKeyId(); - this.session = proto.getSession(); - this.state = proto.getState(); - } - - public synchronized void close() { - isClosed = true; - } - - public void switchMode(int mode) { - if (this.mode != mode) { - this.mode = mode; - onModeChanged(); - } - } - - protected void onModeChanged() { - - } - - public void onSessionChanged(byte[] session) { - if (isClosed) { - return; - } - this.session = session; - } - - public abstract void reloadConnectionInformation(); - - public abstract void resetConnectionBackoff(); - - protected synchronized void onMTMessage(MTMessage message) { - if (isClosed) { - return; - } - if (readInt(message.getContent()) == MTMessagesContainer.CLASS_ID) { - try { - TLObject object = context.deserializeMessage(new ByteArrayInputStream(message.getContent())); - if (object instanceof MTMessagesContainer) { - MTMessagesContainer container = (MTMessagesContainer) object; - for (MTMessage mtMessage : container.getMessages()) { - transportPoolCallback.onMTMessage(mtMessage); - } - } - } catch (IOException e) { - // Ignore this - // Logger.e(TAG, e); - } finally { - BytesCache.getInstance().put(message.getContent()); - } - } else { - transportPoolCallback.onMTMessage(message); - } - - } - - protected synchronized void onFastConfirm(int hash) { - if (isClosed) { - return; - } - transportPoolCallback.onFastConfirm(hash); - } - - @Override - public void onSchedullerUpdated(Scheduller scheduller) { - - } - - - private byte[] optimizedSHA(byte[] serverSalt, byte[] session, long msgId, int seq, int len, byte[] data, int datalen) { - try { - MessageDigest crypt = MessageDigest.getInstance("SHA-1"); - crypt.reset(); - crypt.update(serverSalt); - crypt.update(session); - crypt.update(longToBytes(msgId)); - crypt.update(intToBytes(seq)); - crypt.update(intToBytes(len)); - crypt.update(data, 0, datalen); - return crypt.digest(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - - return null; - } - - protected MTMessage decrypt(byte[] data, int offset, int len) throws IOException { - ByteArrayInputStream stream = new ByteArrayInputStream(data); - stream.skip(offset); - byte[] msgAuthKey = readBytes(8, stream); - for (int i = 0; i < authKeyId.length; i++) { - if (msgAuthKey[i] != authKeyId[i]) { - // Logger.w(TAG, "Unsupported msgAuthKey"); - throw new SecurityException(); - } - } - byte[] msgKey = readBytes(16, stream); - - byte[] sha1_a = SHA1(msgKey, substring(authKey, 8, 32)); - byte[] sha1_b = SHA1(substring(authKey, 40, 16), msgKey, substring(authKey, 56, 16)); - byte[] sha1_c = SHA1(substring(authKey, 72, 32), msgKey); - byte[] sha1_d = SHA1(msgKey, substring(authKey, 104, 32)); - - byte[] aesKey = concat(substring(sha1_a, 0, 8), substring(sha1_b, 8, 12), substring(sha1_c, 4, 12)); - byte[] aesIv = concat(substring(sha1_a, 8, 12), substring(sha1_b, 0, 8), substring(sha1_c, 16, 4), substring(sha1_d, 0, 8)); - - int totalLen = len - 8 - 16; - byte[] encMessage = BytesCache.getInstance().allocate(totalLen); - readBytes(encMessage, 0, totalLen, stream); - - byte[] rawMessage = BytesCache.getInstance().allocate(totalLen); - // long decryptStart = System.currentTimeMillis(); - AES256IGEDecryptBig(encMessage, rawMessage, totalLen, aesIv, aesKey); - // Logger.d(TAG, "Decrypted in " + (System.currentTimeMillis() - decryptStart) + " ms"); - BytesCache.getInstance().put(encMessage); - - ByteArrayInputStream bodyStream = new ByteArrayInputStream(rawMessage); - byte[] serverSalt = readBytes(8, bodyStream); - byte[] session = readBytes(8, bodyStream); - long messageId = readLong(bodyStream); - int mes_seq = StreamingUtils.readInt(bodyStream); - - int msg_len = StreamingUtils.readInt(bodyStream); - - int bodySize = totalLen - 32; - - if (msg_len % 4 != 0) { - throw new SecurityException(); - } - - if (msg_len > bodySize) { - throw new SecurityException(); - } - - if (msg_len - bodySize > 15) { - throw new SecurityException(); - } - - byte[] message = BytesCache.getInstance().allocate(msg_len); - readBytes(message, 0, msg_len, bodyStream); - - BytesCache.getInstance().put(rawMessage); - - byte[] checkHash = optimizedSHA(serverSalt, session, messageId, mes_seq, msg_len, message, msg_len); - - if (!arrayEq(substring(checkHash, 4, 16), msgKey)) { - throw new SecurityException(); - } - - if (!arrayEq(session, this.session)) { - return null; - } - -// if (TimeOverlord.getInstance().getTimeAccuracy() < 10) { -// long time = (messageId >> 32); -// long serverTime = TimeOverlord.getInstance().getServerTime(); -// -// if (serverTime + 30 < time) { -// Logger.w(TAG, "Incorrect message time: " + time + " with server time: " + serverTime); -// // return null; -// } -// -// if (time < serverTime - 300) { -// Logger.w(TAG, "Incorrect message time: " + time + " with server time: " + serverTime); -// // return null; -// } -// } - - return new MTMessage(messageId, mes_seq, message, message.length); - } - - protected EncryptedMessage encrypt(int seqNo, long messageId, byte[] content) throws IOException { - long salt = state.findActualSalt((int) (TimeOverlord.getInstance().getServerTime() / 1000)); - ByteArrayOutputStream messageBody = new ByteArrayOutputStream(); - writeLong(salt, messageBody); - writeByteArray(session, messageBody); - writeLong(messageId, messageBody); - writeInt(seqNo, messageBody); - writeInt(content.length, messageBody); - writeByteArray(content, messageBody); - - byte[] innerData = messageBody.toByteArray(); - byte[] msgKey = substring(SHA1(innerData), 4, 16); - int fastConfirm = readInt(SHA1(innerData)) | (1 << 31); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - writeByteArray(authKeyId, out); - writeByteArray(msgKey, out); - - byte[] sha1_a = SHA1(msgKey, substring(authKey, 0, 32)); - byte[] sha1_b = SHA1(substring(authKey, 32, 16), msgKey, substring(authKey, 48, 16)); - byte[] sha1_c = SHA1(substring(authKey, 64, 32), msgKey); - byte[] sha1_d = SHA1(msgKey, substring(authKey, 96, 32)); - - byte[] aesKey = concat(substring(sha1_a, 0, 8), substring(sha1_b, 8, 12), substring(sha1_c, 4, 12)); - byte[] aesIv = concat(substring(sha1_a, 8, 12), substring(sha1_b, 0, 8), substring(sha1_c, 16, 4), substring(sha1_d, 0, 8)); - - byte[] encoded = AES256IGEEncrypt(align(innerData, 16), aesIv, aesKey); - writeByteArray(encoded, out); - EncryptedMessage res = new EncryptedMessage(); - res.data = out.toByteArray(); - res.fastConfirm = fastConfirm; - return res; - } - - protected class EncryptedMessage { - public byte[] data; - public int fastConfirm; - } -} diff --git a/src/org/telegram/mtproto/transport/TransportPoolCallback.java b/src/org/telegram/mtproto/transport/TransportPoolCallback.java deleted file mode 100644 index 3753eb9..0000000 --- a/src/org/telegram/mtproto/transport/TransportPoolCallback.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.telegram.mtproto.transport; - -import org.telegram.mtproto.tl.MTMessage; - -/** - * Created by ex3ndr on 03.04.14. - */ -public interface TransportPoolCallback { - public void onMTMessage(MTMessage message); - - public void onFastConfirm(int hash); -} diff --git a/src/org/telegram/mtproto/transport/TransportRate.java b/src/org/telegram/mtproto/transport/TransportRate.java deleted file mode 100644 index b039c9a..0000000 --- a/src/org/telegram/mtproto/transport/TransportRate.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.telegram.mtproto.transport; - -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.state.ConnectionInfo; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Random; - -/** - * Created by ex3ndr on 26.11.13. - */ -public class TransportRate { - - private static final String TAG = "TransportRate"; - - private HashMap transports = new HashMap(); - - private Random rnd = new Random(); - - public TransportRate(ConnectionInfo[] connectionInfos) { - int min = Integer.MAX_VALUE; - int max = Integer.MIN_VALUE; - for (int i = 0; i < connectionInfos.length; i++) { - min = Math.min(connectionInfos[i].getPriority(), min); - max = Math.max(connectionInfos[i].getPriority(), max); - } - for (int i = 0; i < connectionInfos.length; i++) { - transports.put(connectionInfos[i].getId(), - new Transport(new ConnectionType(connectionInfos[i].getId(), connectionInfos[i].getAddress(), connectionInfos[i].getPort(), ConnectionType.TYPE_TCP), - connectionInfos[i].getPriority() - min + 1)); - } - normalize(); - } - - public synchronized ConnectionType tryConnection() { - float value = rnd.nextFloat(); - Transport[] currentTransports = transports.values().toArray(new Transport[0]); - Arrays.sort(currentTransports, new Comparator() { - @Override - public int compare(Transport transport, Transport transport2) { - return -Float.compare(transport.getRate(), transport2.getRate()); - } - }); - ConnectionType type = currentTransports[0].getConnectionType(); - Logger.d(TAG, "tryConnection #" + type.getId()); - return type; - } - - public synchronized void onConnectionFailure(int id) { - Logger.d(TAG, "onConnectionFailure #" + id); - transports.get(id).rate *= 0.5; - normalize(); - } - - public synchronized void onConnectionSuccess(int id) { - Logger.d(TAG, "onConnectionSuccess #" + id); - transports.get(id).rate *= 1.5; - normalize(); - } - - private synchronized void normalize() { - float sum = 0; - for (Integer id : transports.keySet()) { - sum += transports.get(id).rate; - } - for (Integer id : transports.keySet()) { - Transport transport = transports.get(id); - transport.rate /= sum; - Logger.d(TAG, "Transport: #" + transport.connectionType.getId() + " " + transport.connectionType.getHost() + ":" + transport.getConnectionType().getPort() + " #" + transport.getRate()); - } - } - - private class Transport { - private ConnectionType connectionType; - private float rate; - - private Transport(ConnectionType connectionType, float rate) { - this.connectionType = connectionType; - this.rate = rate; - } - - public ConnectionType getConnectionType() { - return connectionType; - } - - public void setConnectionType(ConnectionType connectionType) { - this.connectionType = connectionType; - } - - public float getRate() { - return rate; - } - - public void setRate(float rate) { - this.rate = rate; - } - } -} diff --git a/src/org/telegram/mtproto/transport/TransportTcpPool.java b/src/org/telegram/mtproto/transport/TransportTcpPool.java deleted file mode 100644 index 70fe08c..0000000 --- a/src/org/telegram/mtproto/transport/TransportTcpPool.java +++ /dev/null @@ -1,390 +0,0 @@ -package org.telegram.mtproto.transport; - -import com.droidkit.actors.*; -import org.telegram.mtproto.MTProto; -import org.telegram.mtproto.backoff.ExponentalBackoff; -import org.telegram.mtproto.log.Logger; -import org.telegram.mtproto.schedule.PrepareSchedule; -import org.telegram.mtproto.schedule.PreparedPackage; -import org.telegram.mtproto.schedule.Scheduller; -import org.telegram.mtproto.secure.Entropy; -import org.telegram.mtproto.tl.MTMessage; -import org.telegram.mtproto.tl.MTPing; - -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; - -import static org.telegram.mtproto.util.TimeUtil.getUnixTime; - -/** - * Created by ex3ndr on 03.04.14. - */ -public class TransportTcpPool extends TransportPool { - - private static final boolean LOG_SCHEDULLER = true; - - private final String TAG; - private static final boolean USE_CHECKSUM = false; - private static final int LOW_TIME_DIE_CHECK = 30 * 1000; // 30 sec - - private int desiredConnectionCount; - - private final HashSet contexts = new HashSet(); - private final HashMap contextConnectionId = new HashMap(); - private final HashSet connectedContexts = new HashSet(); - private final HashSet initedContext = new HashSet(); - - private static final int PING_TIMEOUT = 60 * 1000; - - private ActorSystem actorSystem; - - private TransportRate connectionRate; - - private TcpListener tcpListener; - - private ActorRef connectionActor; - private ActorRef scheduleActor; -// private ConnectionActor.ConnectorMessenger connectionActor; -// private SchedullerActor.Messenger scheduleActor; - - private ExponentalBackoff exponentalBackoff; - - public TransportTcpPool(MTProto proto, TransportPoolCallback callback, int connectionCount) { - super(proto, callback); - TAG = "TransportTcpPool#" + proto.getInstanceIndex(); - this.exponentalBackoff = new ExponentalBackoff(TAG); - this.desiredConnectionCount = connectionCount; - this.actorSystem = proto.getActorSystem(); - this.tcpListener = new TcpListener(); - this.connectionRate = new TransportRate(proto.getState().getAvailableConnections()); - - connectionActor = actorSystem.actorOf(connection()); - scheduleActor = actorSystem.actorOf(scheduller()); - } - - @Override - public void onSchedullerUpdated(Scheduller scheduller) { - if (LOG_SCHEDULLER) { - Logger.d(TAG, "onSchedullerUpdated"); - } - scheduleActor.send(new Schedule()); - synchronized (contexts) { - if (contexts.size() == 0) { - this.connectionActor.send(new CheckConnections()); - } - } - this.connectionActor.sendOnce(new CheckDestroy(), LOW_TIME_DIE_CHECK); - } - - @Override - public void reloadConnectionInformation() { - this.connectionRate = new TransportRate(proto.getState().getAvailableConnections()); - } - - @Override - public void resetConnectionBackoff() { - exponentalBackoff.reset(); - } - - @Override - protected void onModeChanged() { - this.scheduleActor.send(new Schedule()); - this.connectionActor.send(new CheckConnections()); - this.connectionActor.sendOnce(new CheckDestroy(), LOW_TIME_DIE_CHECK); - } - - private ActorSelection connection() { - return new ActorSelection(Props.create(ConnectionActor.class, new ActorCreator() { - @Override - public ConnectionActor create() { - return new ConnectionActor(TransportTcpPool.this); - } - }).changeDispatcher("connection"), - "tcp_connection_" + proto.getInstanceIndex()); - } - - private ActorSelection scheduller() { - return new ActorSelection(Props.create(SchedullerActor.class, new ActorCreator() { - @Override - public SchedullerActor create() { - return new SchedullerActor(TransportTcpPool.this); - } - }), "tcp_scheduller_" + proto.getInstanceIndex()); - } - - private static final class CheckDestroy { - - } - - private static final class CheckConnections { - - } - - private static final class Schedule { - - } - - private static class ConnectionActor extends Actor { - private TransportTcpPool pool; - - private ConnectionActor(TransportTcpPool pool) { - this.pool = pool; - } - - @Override - public void preStart() { - self().send(new CheckConnections()); - } - - @Override - public void onReceive(Object message) { - if (message instanceof CheckDestroy) { - onCheckDestroyMessage(); - } else if (message instanceof CheckConnections) { - onCheckMessage(); - } - } - - protected void onCheckDestroyMessage() { - if (pool.mode == MODE_LOWMODE) { - if (pool.scheduller.hasRequests()) { - self().send(new CheckDestroy(), LOW_TIME_DIE_CHECK); - return; - } - - // Logger.d(TAG, "Destroying contexts"); - synchronized (pool.contexts) { - for (TcpContext c : pool.contexts) { - c.close(); - } - pool.contexts.clear(); - } - } - } - - protected void onCheckMessage() { - try { - if (pool.mode == MODE_LOWMODE) { - if (!pool.scheduller.hasRequests()) { - // Logger.d(TAG, "Ignoring context check: scheduller is empty in low mode."); - return; - } - } - - synchronized (pool.contexts) { - if (pool.contexts.size() >= pool.desiredConnectionCount) { - // Logger.d(TAG, "Ignoring context check: already created enough contexts."); - return; - } - } - - ConnectionType type = pool.connectionRate.tryConnection(); - // Logger.d(TAG, "Creating context for #" + type.getId() + " " + type.getHost() + ":" + type.getPort()); - try { - TcpContext context = new TcpContext(pool.proto, type.getHost(), type.getPort(), USE_CHECKSUM, pool.tcpListener); - // Logger.d(TAG, "Context created."); - synchronized (pool.contexts) { - pool.contexts.add(context); - pool.contextConnectionId.put(context.getContextId(), type.getId()); - } - pool.scheduller.postMessageDelayed(new MTPing(Entropy.generateRandomId()), false, PING_TIMEOUT, 0, context.getContextId(), false); - } catch (IOException e) { - // Logger.d(TAG, "Context create failure."); - pool.connectionRate.onConnectionFailure(type.getId()); - throw e; - } - - // messenger().check(); - self().send(new CheckConnections()); - } catch (Exception e) { - self().send(new CheckConnections(), 1000); - } - } - } - - private static class SchedullerActor extends Actor { - private TransportTcpPool pool; - private PrepareSchedule prepareSchedule = new PrepareSchedule(); - private int roundRobin = 0; - - private SchedullerActor(TransportTcpPool pool) { - this.pool = pool; - } - - @Override - public void preStart() { - self().send(new Schedule()); - } - - @Override - public void onReceive(Object message) { - if (message instanceof Schedule) { - onScheduleMessage(); - } - } - - public void onScheduleMessage() { - // Logger.d(TAG, "onScheduleMessage"); - int[] contextIds; - synchronized (pool.contexts) { - TcpContext[] currentContexts = pool.contexts.toArray(new TcpContext[0]); - contextIds = new int[currentContexts.length]; - for (int i = 0; i < contextIds.length; i++) { - contextIds[i] = currentContexts[i].getContextId(); - } - } - - pool.scheduller.prepareScheduller(prepareSchedule, contextIds); - if (prepareSchedule.isDoWait()) { -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "Scheduller:wait " + prepareSchedule.getDelay()); -// } - self().sendOnce(new Schedule(), prepareSchedule.getDelay()); - return; - } - - TcpContext context = null; - synchronized (pool.contexts) { - TcpContext[] currentContexts = pool.contexts.toArray(new TcpContext[0]); - outer: - for (int i = 0; i < currentContexts.length; i++) { - int index = (i + roundRobin + 1) % currentContexts.length; - for (int allowed : prepareSchedule.getAllowedContexts()) { - if (currentContexts[index].getContextId() == allowed) { - context = currentContexts[index]; - break outer; - } - } - - } - - if (currentContexts.length != 0) { - roundRobin = (roundRobin + 1) % currentContexts.length; - } - } - - if (context == null) { -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "Scheduller: no context"); -// } -// messenger().schedule(); - self().sendOnce(new Schedule()); - return; - } - -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "doSchedule"); -// } - - long start = System.currentTimeMillis(); - PreparedPackage preparedPackage = pool.scheduller.doSchedule(context.getContextId(), pool.initedContext.contains(context.getContextId())); -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "Schedulled in " + (System.currentTimeMillis() - start) + " ms"); -// } - if (preparedPackage == null) { -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "No packages for scheduling"); -// } -// messenger().schedule(); - self().sendOnce(new Schedule()); - return; - } - -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "MessagePushed (#" + context.getContextId() + "): time:" + getUnixTime(preparedPackage.getMessageId())); -// Logger.d(TAG, "MessagePushed (#" + context.getContextId() + "): seqNo:" + preparedPackage.getSeqNo() + ", msgId" + preparedPackage.getMessageId()); -// } - - try { - EncryptedMessage msg = pool.encrypt(preparedPackage.getSeqNo(), preparedPackage.getMessageId(), preparedPackage.getContent()); - if (preparedPackage.isHighPriority()) { - pool.scheduller.registerFastConfirm(preparedPackage.getMessageId(), msg.fastConfirm); - } - if (!context.isClosed()) { - context.postMessage(msg.data, preparedPackage.isHighPriority()); - pool.initedContext.add(context.getContextId()); - } else { - pool.scheduller.onConnectionDies(context.getContextId()); - } - } catch (IOException e) { - // Logger.e(TAG, e); - } - -// if (LOG_SCHEDULLER) { -// Logger.d(TAG, "doSchedule end"); -// } - - self().sendOnce(new Schedule()); - } - } - - private class TcpListener implements TcpContextCallback { - - @Override - public void onRawMessage(byte[] data, int offset, int len, TcpContext context) { - if (isClosed) { - return; - } - try { - MTMessage decrypted = decrypt(data, offset, len); - if (decrypted == null) { - Logger.d(TAG, "message ignored"); - return; - } - if (!connectedContexts.contains(context.getContextId())) { - connectedContexts.add(context.getContextId()); - exponentalBackoff.onSuccess(); - connectionRate.onConnectionSuccess(contextConnectionId.get(context.getContextId())); - } - - onMTMessage(decrypted); - } catch (IOException e) { - Logger.e(TAG, e); - synchronized (contexts) { - context.close(); - if (!connectedContexts.contains(context.getContextId())) { - exponentalBackoff.onFailureNoWait(); - connectionRate.onConnectionFailure(contextConnectionId.get(context.getContextId())); - } - contexts.remove(context); - connectionActor.send(new CheckConnections()); - scheduller.onConnectionDies(context.getContextId()); - } - } - } - - @Override - public void onError(int errorCode, TcpContext context) { - // Fully maintained at transport level: TcpContext dies - } - - @Override - public void onChannelBroken(TcpContext context) { - if (isClosed) { - return; - } - int contextId = context.getContextId(); - Logger.d(TAG, "onChannelBroken (#" + contextId + ")"); - synchronized (contexts) { - contexts.remove(context); - if (!connectedContexts.contains(contextId)) { - if (contextConnectionId.containsKey(contextId)) { - exponentalBackoff.onFailureNoWait(); - connectionRate.onConnectionFailure(contextConnectionId.get(contextId)); - } - } - connectionActor.send(new CheckConnections()); - } - scheduller.onConnectionDies(context.getContextId()); - } - - @Override - public void onFastConfirm(int hash) { - if (isClosed) { - return; - } - TransportTcpPool.this.onFastConfirm(hash); - } - } -} \ No newline at end of file diff --git a/src/org/telegram/mtproto/util/BytesCache.java b/src/org/telegram/mtproto/util/BytesCache.java deleted file mode 100644 index 8044fe9..0000000 --- a/src/org/telegram/mtproto/util/BytesCache.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.telegram.mtproto.util; - -import org.telegram.mtproto.log.Logger; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentSkipListSet; - -/** - * Created by ex3ndr on 04.02.14. - */ -public class BytesCache { - - public static BytesCache getInstance() { - return instance; - } - - private static BytesCache instance = new BytesCache("GlobalByteCache"); - - private final int[] SIZES = new int[]{64, 128, 3072, 20 * 1024, 40 * 1024}; - private final int MAX_SIZE = 40 * 1024; - private final boolean TRACK_ALLOCATIONS = false; - - private HashMap> fastBuffers = new HashMap>(); - private HashSet mainFilter = new HashSet(); - private HashSet byteBuffer = new HashSet(); - private WeakHashMap references = new WeakHashMap(); - - private final String TAG; - - public BytesCache(String logTag) { - TAG = logTag; - for (int i = 0; i < SIZES.length; i++) { - fastBuffers.put(SIZES[i], new HashSet()); - } - } - - public synchronized void put(byte[] data) { - references.remove(data); - - if (mainFilter.add(data)) { - for (Integer i : SIZES) { - if (data.length == i) { - fastBuffers.get(i).add(data); - return; - } - } - if (data.length <= MAX_SIZE) { - return; - } - byteBuffer.add(data); - } - } - - public synchronized byte[] allocate(int minSize) { - if (minSize <= MAX_SIZE) { - for (int i = 0; i < SIZES.length; i++) { - if (minSize < SIZES[i]) { - if (!fastBuffers.get(SIZES[i]).isEmpty()) { - Iterator interator = fastBuffers.get(SIZES[i]).iterator(); - byte[] res = interator.next(); - interator.remove(); - - mainFilter.remove(res); - - if (TRACK_ALLOCATIONS) { - references.put(res, Thread.currentThread().getStackTrace()); - } - - return res; - } - - return new byte[SIZES[i]]; - } - } - } else { - byte[] res = null; - for (byte[] cached : byteBuffer) { - if (cached.length < minSize) { - continue; - } - if (res == null) { - res = cached; - } else if (res.length > cached.length) { - res = cached; - } - } - - if (res != null) { - byteBuffer.remove(res); - mainFilter.remove(res); - if (TRACK_ALLOCATIONS) { - references.put(res, Thread.currentThread().getStackTrace()); - } - return res; - } - } - - return new byte[minSize]; - } -} diff --git a/src/org/telegram/mtproto/util/TimeUtil.java b/src/org/telegram/mtproto/util/TimeUtil.java deleted file mode 100644 index 3ec4335..0000000 --- a/src/org/telegram/mtproto/util/TimeUtil.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.telegram.mtproto.util; - -/** - * Created by ex3ndr on 13.11.13. - */ -public class TimeUtil { - public static int getUnixTime(long msgId) { - return (int) (msgId >> 32); - } -}