From e940b3f29230b695997f3c879c4ee22b7fd09928 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 12 May 2018 02:27:04 +0400 Subject: [PATCH] no message --- LegacyComponents.xcodeproj/project.pbxproj | 56 ++ LegacyComponents/LegacyComponents.h | 3 + .../ocr_nn.bin | Bin 0 -> 135656 bytes LegacyComponents/TGCameraController.m | 7 + LegacyComponents/TGMediaAssetsController.m | 11 + LegacyComponents/TGMediaVideoConverter.h | 1 + LegacyComponents/TGMediaVideoConverter.m | 6 +- LegacyComponents/TGPassportAttachMenu.h | 10 +- LegacyComponents/TGPassportAttachMenu.m | 14 +- LegacyComponents/TGPassportMRZ.h | 25 + LegacyComponents/TGPassportMRZ.m | 316 +++++++++ LegacyComponents/TGPassportOCR.h | 8 + LegacyComponents/TGPassportOCR.mm | 21 + LegacyComponents/TGPassportScanView.h | 13 + LegacyComponents/TGPassportScanView.m | 98 +++ LegacyComponents/fast-edge.cpp | 550 +++++++++++++++ LegacyComponents/fast-edge.h | 61 ++ LegacyComponents/genann.c | 357 ++++++++++ LegacyComponents/genann.h | 110 +++ LegacyComponents/ocr.h | 11 + LegacyComponents/ocr.mm | 645 ++++++++++++++++++ 21 files changed, 2312 insertions(+), 11 deletions(-) create mode 100755 LegacyComponents/Resources/LegacyComponentsResources.bundle/ocr_nn.bin create mode 100644 LegacyComponents/TGPassportMRZ.h create mode 100644 LegacyComponents/TGPassportMRZ.m create mode 100644 LegacyComponents/TGPassportOCR.h create mode 100644 LegacyComponents/TGPassportOCR.mm create mode 100644 LegacyComponents/TGPassportScanView.h create mode 100644 LegacyComponents/TGPassportScanView.m create mode 100755 LegacyComponents/fast-edge.cpp create mode 100755 LegacyComponents/fast-edge.h create mode 100755 LegacyComponents/genann.c create mode 100755 LegacyComponents/genann.h create mode 100644 LegacyComponents/ocr.h create mode 100755 LegacyComponents/ocr.mm diff --git a/LegacyComponents.xcodeproj/project.pbxproj b/LegacyComponents.xcodeproj/project.pbxproj index c7616a3487..365a259e90 100644 --- a/LegacyComponents.xcodeproj/project.pbxproj +++ b/LegacyComponents.xcodeproj/project.pbxproj @@ -7,8 +7,20 @@ objects = { /* Begin PBXBuildFile section */ + 09053D7920A5CCF10029652D /* genann.c in Sources */ = {isa = PBXBuildFile; fileRef = 09053D7120A5CCEF0029652D /* genann.c */; }; + 09053D7A20A5CCF10029652D /* fast-edge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09053D7220A5CCEF0029652D /* fast-edge.cpp */; }; + 09053D7B20A5CCF10029652D /* TGPassportOCR.h in Headers */ = {isa = PBXBuildFile; fileRef = 09053D7320A5CCF00029652D /* TGPassportOCR.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09053D7C20A5CCF10029652D /* ocr.h in Headers */ = {isa = PBXBuildFile; fileRef = 09053D7420A5CCF00029652D /* ocr.h */; }; + 09053D7D20A5CCF10029652D /* TGPassportOCR.mm in Sources */ = {isa = PBXBuildFile; fileRef = 09053D7520A5CCF00029652D /* TGPassportOCR.mm */; }; + 09053D7E20A5CCF10029652D /* genann.h in Headers */ = {isa = PBXBuildFile; fileRef = 09053D7620A5CCF00029652D /* genann.h */; }; + 09053D7F20A5CCF10029652D /* fast-edge.h in Headers */ = {isa = PBXBuildFile; fileRef = 09053D7720A5CCF00029652D /* fast-edge.h */; }; + 09053D8020A5CCF10029652D /* ocr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 09053D7820A5CCF00029652D /* ocr.mm */; }; 090671C41F67F71700CCF2F5 /* TGLocationOptionsView.h in Headers */ = {isa = PBXBuildFile; fileRef = 090671C21F67F71700CCF2F5 /* TGLocationOptionsView.h */; }; 090671C51F67F71700CCF2F5 /* TGLocationOptionsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 090671C31F67F71700CCF2F5 /* TGLocationOptionsView.m */; }; + 0916FEA720A1EA7B0084A755 /* TGPassportScanView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0916FEA520A1EA7B0084A755 /* TGPassportScanView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0916FEA820A1EA7B0084A755 /* TGPassportScanView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0916FEA620A1EA7B0084A755 /* TGPassportScanView.m */; }; + 0916FEAB20A1EBFA0084A755 /* TGPassportMRZ.m in Sources */ = {isa = PBXBuildFile; fileRef = 0916FEA920A1EBF90084A755 /* TGPassportMRZ.m */; }; + 0916FEAC20A1EBFA0084A755 /* TGPassportMRZ.h in Headers */ = {isa = PBXBuildFile; fileRef = 0916FEAA20A1EBFA0084A755 /* TGPassportMRZ.h */; settings = {ATTRIBUTES = (Public, ); }; }; 09750F761F2FA816001B9886 /* SSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09750F741F2FA5E8001B9886 /* SSignalKit.framework */; }; 09750FB71F30DB0E001B9886 /* TGClipboardGalleryModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 09750FAF1F30DB0E001B9886 /* TGClipboardGalleryModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 09750FB81F30DB0E001B9886 /* TGClipboardGalleryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 09750FB01F30DB0E001B9886 /* TGClipboardGalleryModel.m */; }; @@ -1179,8 +1191,20 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 09053D7120A5CCEF0029652D /* genann.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = genann.c; sourceTree = ""; }; + 09053D7220A5CCEF0029652D /* fast-edge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "fast-edge.cpp"; sourceTree = ""; }; + 09053D7320A5CCF00029652D /* TGPassportOCR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPassportOCR.h; sourceTree = ""; }; + 09053D7420A5CCF00029652D /* ocr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ocr.h; sourceTree = ""; }; + 09053D7520A5CCF00029652D /* TGPassportOCR.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGPassportOCR.mm; sourceTree = ""; }; + 09053D7620A5CCF00029652D /* genann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = genann.h; sourceTree = ""; }; + 09053D7720A5CCF00029652D /* fast-edge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "fast-edge.h"; sourceTree = ""; }; + 09053D7820A5CCF00029652D /* ocr.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ocr.mm; sourceTree = ""; }; 090671C21F67F71700CCF2F5 /* TGLocationOptionsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationOptionsView.h; sourceTree = ""; }; 090671C31F67F71700CCF2F5 /* TGLocationOptionsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationOptionsView.m; sourceTree = ""; }; + 0916FEA520A1EA7B0084A755 /* TGPassportScanView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TGPassportScanView.h; sourceTree = ""; }; + 0916FEA620A1EA7B0084A755 /* TGPassportScanView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TGPassportScanView.m; sourceTree = ""; }; + 0916FEA920A1EBF90084A755 /* TGPassportMRZ.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPassportMRZ.m; sourceTree = ""; }; + 0916FEAA20A1EBFA0084A755 /* TGPassportMRZ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPassportMRZ.h; sourceTree = ""; }; 09750F741F2FA5E8001B9886 /* SSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SSignalKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegraph-grvwvmixbmcefwboxkzfazvpcrxb/Build/Products/Release Hockeyapp-iphonesimulator/SSignalKit.framework"; sourceTree = ""; }; 09750FAF1F30DB0E001B9886 /* TGClipboardGalleryModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGClipboardGalleryModel.h; sourceTree = ""; }; 09750FB01F30DB0E001B9886 /* TGClipboardGalleryModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGClipboardGalleryModel.m; sourceTree = ""; }; @@ -2365,6 +2389,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 093134BC20A1EA3F003045EE /* Passport */ = { + isa = PBXGroup; + children = ( + 09053D7220A5CCEF0029652D /* fast-edge.cpp */, + 09053D7720A5CCF00029652D /* fast-edge.h */, + 09053D7120A5CCEF0029652D /* genann.c */, + 09053D7620A5CCF00029652D /* genann.h */, + 09053D7420A5CCF00029652D /* ocr.h */, + 09053D7820A5CCF00029652D /* ocr.mm */, + 09053D7320A5CCF00029652D /* TGPassportOCR.h */, + 09053D7520A5CCF00029652D /* TGPassportOCR.mm */, + 0916FEA520A1EA7B0084A755 /* TGPassportScanView.h */, + 0916FEA620A1EA7B0084A755 /* TGPassportScanView.m */, + 0916FEAA20A1EBFA0084A755 /* TGPassportMRZ.h */, + 0916FEA920A1EBF90084A755 /* TGPassportMRZ.m */, + ); + name = Passport; + sourceTree = ""; + }; 09750FAE1F30DAE1001B9886 /* Clipboard Menu */ = { isa = PBXGroup; children = ( @@ -2406,6 +2449,7 @@ D01777291F1F8F100044446D /* LegacyComponents */ = { isa = PBXGroup; children = ( + 093134BC20A1EA3F003045EE /* Passport */, D0EB409E1F2FC0AA00838FE6 /* Resources */, D017776F1F1F91B00044446D /* Utils */, D01779B01F2139720044446D /* POP */, @@ -3908,6 +3952,7 @@ D01778191F1F961D0044446D /* TGMessageEntityHashtag.h in Headers */, D07BCA341F2A9B0400ED97AA /* TGModernGalleryEditableItemView.h in Headers */, D0177A911F221BB10044446D /* TGModernGalleryTransitionView.h in Headers */, + 0916FEAC20A1EBFA0084A755 /* TGPassportMRZ.h in Headers */, D01778E51F20CAE60044446D /* TGNavigationController.h in Headers */, D01777721F1F92420044446D /* TGPhoneUtils.h in Headers */, D07BC7721F2A2B3700ED97AA /* TGPhotoEditorCurvesHistogramView.h in Headers */, @@ -3949,11 +3994,13 @@ D01779261F20FE480044446D /* TGObserverProxy.h in Headers */, D07BCADF1F2B4F5E00ED97AA /* TGLegacyCameraController.h in Headers */, D017782F1F1F961D0044446D /* TGReplyMarkupAttachment.h in Headers */, + 0916FEA720A1EA7B0084A755 /* TGPassportScanView.h in Headers */, D07BCB211F2B646A00ED97AA /* TGPasscodeBackground.h in Headers */, D01778371F1F961D0044446D /* TGAudioMediaAttachment.h in Headers */, D07BC6F51F2A19A700ED97AA /* TGCameraSegmentsView.h in Headers */, D04269051F586A070037ECE8 /* TGVideoMessageScrubber.h in Headers */, D07BC87F1F2A365000ED97AA /* TGProgressWindow.h in Headers */, + 09053D7F20A5CCF10029652D /* fast-edge.h in Headers */, D07BC8001F2A2C0B00ED97AA /* PGGrainTool.h in Headers */, D01779EA1F2139980044446D /* POPAnimationPrivate.h in Headers */, D017775C1F1F8FE60044446D /* PSKeyValueStore.h in Headers */, @@ -4011,6 +4058,7 @@ 090671C41F67F71700CCF2F5 /* TGLocationOptionsView.h in Headers */, D07BCAAE1F2B45DA00ED97AA /* TGFileUtils.h in Headers */, D0177B1E1F2641B10044446D /* PGCameraMovieWriter.h in Headers */, + 09053D7B20A5CCF10029652D /* TGPassportOCR.h in Headers */, D01779641F2103910044446D /* TGPaintUtils.h in Headers */, D07BCB741F2B6A5600ED97AA /* TGEmbedYoutubePlayerView.h in Headers */, D07BC8C51F2A37EC00ED97AA /* TGPhotoPaintEntityView.h in Headers */, @@ -4050,6 +4098,7 @@ D0177A071F2139980044446D /* POPSpringAnimation.h in Headers */, D07BC9951F2A481C00ED97AA /* TGStickerKeyboardTabSettingsCell.h in Headers */, D0177A341F21F1980044446D /* UIImage+TGMediaEditableItem.h in Headers */, + 09053D7E20A5CCF10029652D /* genann.h in Headers */, D07BC94E1F2A3EA900ED97AA /* TGHashtagPanelCell.h in Headers */, D07BCBAF1F2B6F6300ED97AA /* CBCoubAudioSource.h in Headers */, D07BC7841F2A2B3700ED97AA /* TGPhotoEditorSliderView.h in Headers */, @@ -4117,6 +4166,7 @@ D01779FE1F2139980044446D /* POPGeometry.h in Headers */, D07BCA981F2B443700ED97AA /* TGMediaAssetsPickerController.h in Headers */, D07BCBF51F2B72DC00ED97AA /* STKLocalFileDataSource.h in Headers */, + 09053D7C20A5CCF10029652D /* ocr.h in Headers */, D07BC9F11F2A9A2B00ED97AA /* TGMediaPickerCell.h in Headers */, D07BC8371F2A2D0C00ED97AA /* TGSecretTimerPickerItemView.h in Headers */, D07BC8D31F2A37EC00ED97AA /* TGPhotoPaintTextEntity.h in Headers */, @@ -4690,6 +4740,7 @@ D07BC9BB1F2A705D00ED97AA /* TGPhotoFilterCell.m in Sources */, D0177A571F21F7F40044446D /* TGDoubleTapGestureRecognizer.m in Sources */, D07BC7FF1F2A2C0B00ED97AA /* PGFadeTool.m in Sources */, + 0916FEAB20A1EBFA0084A755 /* TGPassportMRZ.m in Sources */, D01778C31F200AF70044446D /* TGAnimationBlockDelegate.m in Sources */, D07BC85F1F2A2DBD00ED97AA /* TGMenuSheetTitleItemView.m in Sources */, D07BC6EC1F2A19A700ED97AA /* TGCameraFlashActiveView.m in Sources */, @@ -4821,6 +4872,7 @@ D07BC85D1F2A2DBD00ED97AA /* TGMenuSheetItemView.m in Sources */, D07BC90E1F2A380D00ED97AA /* TGPainting.m in Sources */, D0177A441F21F62A0044446D /* TGMediaVideoConverter.m in Sources */, + 09053D8020A5CCF10029652D /* ocr.mm in Sources */, D01779A51F210A120044446D /* TGMediaAssetLegacyImageSignals.m in Sources */, D017777B1F1F927A0044446D /* NSObject+TGLock.m in Sources */, D01778281F1F961D0044446D /* TGMessageEntitiesAttachment.m in Sources */, @@ -4836,6 +4888,7 @@ D07BC7181F2A29B700ED97AA /* TGPhotoEditorController.m in Sources */, D07BC8251F2A2C0B00ED97AA /* PGSharpenTool.m in Sources */, D0177AF41F23DF6D0044446D /* TGImageManagerTask.m in Sources */, + 09053D7920A5CCF10029652D /* genann.c in Sources */, D017783F1F1F961D0044446D /* TGDocumentAttributeImageSize.m in Sources */, D01778E61F20CAE60044446D /* TGNavigationController.m in Sources */, D07BC8C61F2A37EC00ED97AA /* TGPhotoPaintEntityView.m in Sources */, @@ -4922,6 +4975,7 @@ D07BCB361F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m in Sources */, D07BC90A1F2A380D00ED97AA /* TGPaintFaceDebugView.m in Sources */, D017785A1F1F961D0044446D /* TGImageMediaAttachment.m in Sources */, + 09053D7D20A5CCF10029652D /* TGPassportOCR.mm in Sources */, D017784C1F1F961D0044446D /* TGUnsupportedMediaAttachment.m in Sources */, D01779151F20F4500044446D /* TGStaticBackdropAreaData.m in Sources */, D07BC91C1F2A380D00ED97AA /* TGPaintRender.m in Sources */, @@ -5031,6 +5085,7 @@ D01778AA1F1FD0900044446D /* TGImageUtils.mm in Sources */, D07BCADA1F2B4F2800ED97AA /* TGOverlayFormsheetController.m in Sources */, D07BC8571F2A2DBD00ED97AA /* TGMenuSheetButtonItemView.m in Sources */, + 0916FEA820A1EA7B0084A755 /* TGPassportScanView.m in Sources */, D07BCA161F2A9A2B00ED97AA /* TGMediaPickerPhotoStripCell.m in Sources */, D07BCBAD1F2B6F6300ED97AA /* CBConstance.m in Sources */, D017780A1F1F961D0044446D /* TGGameMediaAttachment.m in Sources */, @@ -5077,6 +5132,7 @@ D07BCBD01F2B6F6300ED97AA /* NSDictionary+CBExtensions.m in Sources */, D07BC7731F2A2B3700ED97AA /* TGPhotoEditorCurvesHistogramView.m in Sources */, D07BCB3E1F2B65F100ED97AA /* TGWallpaperInfo.m in Sources */, + 09053D7A20A5CCF10029652D /* fast-edge.cpp in Sources */, D07BCA1C1F2A9A2B00ED97AA /* TGMediaPickerSelectionGestureRecognizer.m in Sources */, D07BC9141F2A380D00ED97AA /* TGPaintNeonBrush.m in Sources */, D01778161F1F961D0044446D /* TGMessageEntityCode.m in Sources */, diff --git a/LegacyComponents/LegacyComponents.h b/LegacyComponents/LegacyComponents.h index 5e6f85df1c..4b4d9175a4 100644 --- a/LegacyComponents/LegacyComponents.h +++ b/LegacyComponents/LegacyComponents.h @@ -278,6 +278,9 @@ FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[]; #import #import #import +#import +#import +#import #import #import #import diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/ocr_nn.bin b/LegacyComponents/Resources/LegacyComponentsResources.bundle/ocr_nn.bin new file mode 100755 index 0000000000000000000000000000000000000000..ccab89e55470cec181f288c65fc62fb212992f18 GIT binary patch literal 135656 zcmWKXhdY&j7{*CfQC1n12qmM?P{Kpyr-VYK%pxg?QXwHKnaK{3Y$AKF$KK;`IGkgh zV<#b_iGJq~IODqB@B4i3`~KX|%@kgvP|i6#EV@&0yn7Ne?V3HJ?GU)*D;|oiWT5DO zPfm1UF?gzFea}no!KaO_mz>kbu%GdtDCp;atVFSdKSKw^&afWt^d12Bbf49+)2X=6 zQr+)}N*%+TiQ=id;Y69*~G${iDb{4Ta>#iMbO) zkng6zlAW7~$$yh;(r*so8}9s1c0BEn@BbutPk$p`Xq4JLp!gp3%!19X3lG7*%@Xy* z%Oya{ww2rbv=}MfmyB1hbYmZ_Q|xVO16J26)BoCMDsUf-4u9tGJy%b`xWy+HDz z{q^)6K$H2#v%4Mo;gKHUSV#yBUTmwgfBvHpr#!Z9+V6t+BE`vWbaw{cYhM~SkE%r0 z!{P}HvMqS#I;(5#bUNJTZ)iQF(E`cL+?U)=R$^0mfzGAC4(NO5`-~%C0!l`2O!(fJ zfD?h^Ro_yGsQSj!BCVqtPTHn#Efmbh+$)@?%g1~$ufXg0cw7yJb{{|M8$kvCTf5$! z&AN?xBhN2MauHGPuiEu3TM3xmN0STXn}US1E2SQlR2(OL#x(T^F$J~#x@bKHc~H|02PBVIWe-ij9&8=tcGjX}_8MPf4H0P6%^ZQGlHoqPax*8QM{cH#{oEx)K&*VYU zQ(Q4762YEabmFjFKe%(YGhd#cfINjnd|E~YnVf$Qnnijs<(59vxr6;^8|Zn=TcZ$_ z^i4ZoId_07hvsk#i2%A?Z$5Se4?%U3{f_g69VoaG^kYj_4yHcyEH5S!v4+Je?$(O} z;Ids@zo7IP#yhVGpKHrQ>tFdd-ydv8(YxQ$Zut#hh1PLnqbLG?_-Pk>yt4ofKNDVZ znra7QnImWAgl14Pa^Z4QRXb|3?qu(vrNL#>tomENWcYkvIQN-VHLyJBYF_;MiT?gY z94(LQ0M#p&xvL)Ka6>FKn#r^eY$Vwd72Ud!`2(-m&?E(&o^B|y1cbtP@yAh}rcqo@ zSs$U54gU2aTL1$n_20>UjqHa4u?s6@#d9F5nmVd^){JoR{sK$ z52`las`sFqEQg{o{}jebzLBUsKZNfLPM?UbCcqJHcFX?Tyam4139&N!_GnZqJZWt5Z`wM5>XQ2f`50QEZZ=EAD7YCU^J^KynaHqTh-YEeu?blu zcZ)J>Btiaj%Ne(tB6xCp$HG?kER0*nn{7pXNIhjT^&ILQCiiwYXsC6G{kQ!_ux;7^TOO$no&QZ;IPVU zKW-1XwCj38ElwFQZ|gqXjbXCcQr*?PcwYR=oBIiUP;y&RIAp;QNpJkjpTEq*YX*+J zDhu!MNb~hSx*jCpR_c8^Da++X4dEDxs0uk$tZpVfmYl7VWro9J6hheIG z_Yu;M1}tpbD<7RQ1SkLFy;^!T0hE6{%ktlvh-zi$qm%4vfpl|bGs`&&99eefYEJ3~ z)-1cui{_CyDsQhR`+F45pLSU${px_^sR#NqTMEJdXsOC~nGv`hbR)ROrW<}52W7FJ zsDhcQ`-21rg8W!*)t6=S>CK@ zW!Hvc#twdi(HRiY7XJELT`mkypL9H5-2=u34&EZilW^e>74Do`VXYPI`gO!<*$KM7LC}#PFW^C?qmq*2z zr;#%evLyi@#@*4jIZi=Pz0k1xc{>C;**cwW*T5%|*A3i*2;g#-b&o?s7i|C2`?mQ% zA};T`V|yrM02a1dGoOFZ3rSu4TIYp`FxS?Z#`7~1xKI6axth?6pLrJ~FW+{=0@IL1 zsvi}85SPlM|LZ~nrKArVfk{|X%8`_n*MJ5-HYW~kc?tKmKWH9!*$0Q`r6?Jf^Rad* z<-gWH?@;|bxl2cw2!>xWXdW)P$lIAq+1}WSy2rkbRoafhI&EXPRm#S1PVQ70KJLPen1&mp5jOn?c>rQPW$p1(Kd@&ilqzi#-Xx&N0oA zn4QXZCMdEC&c?fo?L5;6CfQx9O>G^hbEQqnvU~sxUw+%-e76BK&z6S!J|Ke2#fVL* z)592f^w7&{M+)8(l*rcB?S{{dD*Zi<4dDLn_ukiH-Pk(HteH_tLifdn(ieJU47pyR zu>EQd?iJlE=YE=g4$q@6!ygLBrTeQogjZnxNhWsbt<6|4`|=Avp#!{b@6%%8uEB@u z6TDM{eNbCJ?mITs4Qk#p*GI<6aFn_9;SPaLTy{wqT4N#MKZBg;hi1bl00D2<`#W&2 zllSoksc~4ls^R+OLL=tw)!(7cIRwjz5=O%2ZFtu>AVp|TF#P!NVB#-NDcByBwAi_G z02{I^EyQm10?oWn>oRvQ_GJ$YUI`h86NHTNjy7A%EQn2%KU?b@4H#ezWYJ`1X89s4;?*^Se1`2!K+F>psUE|j0ZeXr7a`rJwgs}M~ zH@sVnmTQkIOxLDRnR570(T)l<_@T8qZ8it91XuX)D%65hOzy+zOT&QCRynHIOHp&1 z675OM2qeyF{g5rI1e3SVT7!9;;MOi_^yo;zEuxMoyiL7m5vZ`@I79$xJ zlm3-d@jTo=);4&4q#i)r_`O~SZ@Fn`*?Y4FpHQFCe;1UAa(3RCDf2pP#*nwd7^ z`6Wfo>a|*A%30MmJl6{cws5K?3Z-M3NUrnFak&r24atCAS)WFo@{5coXn={wUXm zEe&N$t(C`gqHyIt!TPyu13n`p)OdgHfgK)#Tt!Elq4ma{2f;_$z~Kq6=G}KgFcEaH zG(2<+ns2_?Qp_<1LP0WvcjuZ=Y&Uc7PP$$bnS5ZhGrSHNmluzpdg%n_y`PG{b;ZCi zS6k?YO&YN6Dpzpa+k`ffaY8HN-5_C;Rnt^E4*5!6S9drOF<9T-cJX^7EUE`h*ok(a zHvbz3iP+Deh3;mD*lWRi*WL>)vt)RCiE&jpeG=b_aeK%wQL$KnKfO`E9YiuV@g|KX zp_$NgzFW~0_^tQknxK3;T843q$hVMyBI|M~;4TH7{dS2~xOT#lK;y1+4s|fGb7z;U zcpok(5{;k5*2B}qY|3Kq82+vMy{uMB#paP`GouFxV8tVOq`RC1p7QV6?j81qmX9e% zh@5?(?&$vLa3&R`EzbulY@3EiuGn4I-zjy^`WZ%E|=tdYA-)!B)@i-R-iw=AKZOVW<2macV zAGYEPd;c1#;!)t2x8bcaNXE2(S{-{HHpBb%xO~cvUc9`KV-l}7fzOns+ZDt+)+K`exjw544$pP^v7Td9J@|1eL_H02 z@0}zgL{F^r;W)mZn64F2>&DJpKU10MI+Q<9R{hE?50p z6@zXv&_>K16#{BeCs*tW@7{5wo$u#nWT8S!PcWC}=>e2ko4VrG+=$#!MW#Y$`!Vz# z`P%5d4isi;-5lwb51of>EizQduvu3};LTA2zT-AsSs=_}a{j&7ZpD+>2<2`*T_IB~ zz<4|5S!6^B{Ow3wJ5wq^!Q7~!8zWg4} zd`u#jnw;^dgK$x9=Xw7wj6PA+HPAtZF9}y1()9bldO*{^UOrriji%2Jb_>vuNa^zQ(MA~X;Opv}^TQ*)GhIiXQ=w9FDbf6N6P|by zYNp0N1}pZLd4@wq@x%PD=*&V2hLxP35CRSux$op6Ljd2x ziSDu<44aX#k*gv?mXKn)eNi9$6g+ZM;!YespS=C#yxkyf_8K=#q<+TVojq^Do62$4 z*zcT_O&#QqO3LR|jG~Q0@czt)UMS=K`nNZE0{B$j%r3vA>+*B;Q{(&UQFlT3tVU=p z@~`eQ`R_mxaKB&`+); zJa@O30G%R%ld;U%-H6~>BkfhrbWasexX3bGN*@i&KOj! z%q#8Z=mwWO>mG&Dc8K$A*`y@c0E<>4_x}s6!G%Po&FUUR$Zhalbs^-zdWS@0CT|ML zeUt9EAyN%@Pb;_0ou=zXsiIS@9d%%-WPR~mcoV9#n+LS*CZXB)lgl3(ui?k#pfBw^1)>|+l~we_A3mId?euK*vCKG>As};;?Qj?n*roc%6t@Hm4%OE z@BBG;mVyx~S)PStB1peD!XT7f1CHN^a&v14cuQ}HXH$C~c)Y#wGK+p5t^Yx(>8l(Z zMm^~)j(+5j?a6YwJPP+!ubMbKr{L;w#?IeGt*}3+%A2E=0%_ba(R!2~d>_{SQ&6TJ zXdS|N+~#EL7w5lpyR-=xC1moly$BfcvaQ^6Fb9_ND-GF;2jS86!0@@gUKko9)P0?& z=NBQHGZs6Vv8OU`O> zcn_2cVPVD35oEZr_DHGXC7fly&OH2{eqVm@^}kb<0PBXd}z?@ZMe ziZy=#$)$t5<642(+ik~CbqjBHP$l);rNXEe7jlmYJ#gUz-zg^=U>%QQlT0-QhICsb|A z#SBT&Iz`SdR2||MY>R3Gi=P~qqY`UTR*8S3tAc``d<35=rH)|anF{wC={<0NlUA^w zEg5V6P^v~Zit)c%N{jFHAV>Ev9N*R80 z$$rVtGypb@Vtq>a-5??>$&t3(4>tyhuWR*6K_F!|hlz=Re#$9%){u|;mt_+g+$xZ9 zQ^q$|&M7!#YRx1n-UO*CyJqj*vVyOLsRyT7%Rpn%_~Ss+C;VsejQ?TgOE7Tb$O`wM zpxgAQf8ys9jGR$m zKX<+f`Psj-T@s;SvcD`#u}~H0e%y5E_2wuLO{J;syfc94zqZFUnSgQzRgZe!Rin-K z@1GNYHh_Z6lML#?F{me&sNa3i0lv(9tpWM)_zByM7v!^H_O>ineuE3?_hIM$V`MzQm-AU< zqX@%mP8Zw^od(UAeFs}#6@iQYgD|02L*QhkYTMdTk9?U=QcN0W07>V6u+?RQmb~6k zx0}6S5zN$_;yppn`&3wl6o{~*d-0DVZy)ron~Gfc6ppvbBdc$=)MGzu?U--w6;O^? zNB-ON902z#vb`F{^ouH-|NQ9lCbOg>yQd#ln8n{bi+TZZi{hiY=L;dzTfE=Tf}WE{ z9xFJbM}o=GSmKAm7L-pp9zF7V01F1=)H;L5@!IxdA%^+6_+lqd(PBA$4uPH2N2Na8 z&+6Y*8aRLns@ocJzx04jMa+eNbYJ%Sa6tfVQws{adG7h7*NTHROgExqvcO4uhwDhf z7;=qO%sMQOU_{H-C+FEma8u2cmex<o2QnP zVSYS~Us8GuP1l5d{p<;_Z)R-dzCZ_P*F^m>ZTSo$LXue(j zDNq1^*`65KsP%$tC+Et6tu%UmWpS}1hYBTBN8SC<4q0pC?~f{1gK?Z=uqF8u&Y!Ol zl=q<^iCDfjKcy6>p0sfHe6NEGxmLROM8CS5FC3Tbul_r7wZfX+C4kqr2F;~E97 zca1tkKPJL9#m&MyUJxMV_}Xw|-zd84PG4^CYKGnuUr(f8BtZZCE3@wnIhgr%+R|@t zKT;H|7N50O!5Mv~2GOD-v~+t}bHX?hOgg~^~Nl3w%k`Z&(l*a_U4n8C3GmIoJ`X;>sr=IgHd2prFjWi#C9g|H)R zwju{>G3ZC4msG@r2$E)Rf_z^nBHo+VNoE&@KKU^%qGec`XN4UyxS|W2JX|e-v zw;fP78f-$ry5@!dR>r{g_Pu`(4W^;8o8c>KOEK(jyRPhajfk={Ve$9PCqN;Jb?SrZ z1n|^6WxrwE3%05qbx$AC=L~Hr!u7}~5>kI|_w367;cdQEBcAQxrTm=D?py^5o-xvP zS|~&VHcD!sY7*>EdzrFK2t$$kJ=&IZU#smnAsH)~h<=3k|_?GAwbW@=bg;n>6@$R92zedhcm02CQ;B-haPrC*+c#rl}YIWhY zrl_)g8lAAc$$InCpaEofculX0?*tHX-<})YJ&3w8!xK-E-=RDC+E%-{4E%bGi^=r> z{kidPX}rnZ3Ka7%?T^G7K~FrXE?006)A@7WjgafWv-K+Bo?9FK*Knvear-D%KD}4G z;Xy*4Z7LVZ|5{Lk+S-%1oDbKXgkIid>4OW~MDEzA)xu4E2~x+d0Sx@Y_wuh;4SI`L zr97-A;mN+eVvNb-IO5>=V)@Yo4$0PNKDbFk`B<+I=wZ=gk*5QYA9>mB*g`tK ze^}V~vA!6n#DVZ5H>oH$ARN)WJcrexXI6e{_o46M_f8jE`@v9tp(4jO3}^N}iDu^~ z;HGt3U8CJ)__Ri0zshz3{K;s1^L&&Bft&(oE&JiEi^u4>Qu4p01HZblR6ve< z^RpI|)Xl$HbEq1o1I7LkmAj!T@4&+GTSLgX@;$MaVF>F!iLx$UY5^U?iNs%QROtP< zoa=MD6Q<0C+f0Reflc-p-jE&vzA7$V^~+-zrgrj%88wTo zP-?C8`okI#k~D00bM2{y+IcZEv9fB^`=HZ4BZ08+V3KvJ0xX1>zONO|L=^7_BCVU+HTGk*pE^Cp>xZ_`>rQMwwPf z%JugOdYgWXsaF+zvM+No|3Uwim@2G`z zEdjK(+lVerjlktos6kSg!lz>LRMX5h2rLrQA^hpXZ}sjw9^CK5l(5XdI`?}p{3qRA z`jAn@vFFrooWUVP@w0rkcspIVfLaK>|w4Yx%aTAA#Zc=(_I$~k); z&3_6)g?V3>xF!OKnX`sbh7j+lB|OZP?1dVhJ>P|I6l0rRK93MB7p_|zesEtm3tk)% zeLW#Q4OXmYB)FWqu>YlSXVWYCJtEOSt7giCJpWe9=d%6y!Msg)HE9?)w@Dt3Y8r!F z)xKjd|3pJj`nTepC-PCRL(H@F@eGt0wlq5W%_B@#o};Ucj`ieIDc*7#}#Venc_{ zJ1^R`2Z&9fp6l}L>yR4Ip7vmIT(5=DzgaCBPW33t`~Ciov*F-(bV#8!rU_16zMAv2 zXdG7VJl{i-4}<$WgWnn0D^ab~Ut=z+57_e_y%L$v$F1)S_B^N`KyD$h>u4MR7RfE< zKISw`Dvs&Bmeh?r*RkT0aRDCO9xCbnFbT|ru5E8vqr!TO*$L4f^n7aQdwiBA8FpC9 za%jglffmP*Yk6!wveK?3ZaqLkfdyV;pKblvN%7HYbRgmfi>C~2y$$$Mp{STgmk1|H zOq6ujT4CN_sFLuB24N@TmvY=mP*~j}yL+Go;@=tXZwRf1)8wqJMsM;V!7{qIO0gI& zxUMLWTk1h3`zQ{1lA!d@I)CPoT0Cgd%+5*oxuNY@-1WBIXnSBWnv2u|$9I$SsJj}_ zPU2uMDVhdnLZ0pFuqy&K$P7I%HI4z7#iq7>9>kjUlT2G!YcW4<_P>&29bj?Pm6`8w z7fN_ZIxlPw!CUtB4BLm4fQS+~yf#l>Rs5^*rW(F)8NEN9 zTL(_cPN|*;`_L`W)NPtt3yd5wWQ8ZgAXHEj|IUpH)~8=FJQ8iixf`33cNJ#f#%%2{ ziIoC+lR3B79jQ1{m*Z;8H;k_5=F82tj$xt1su`Dj5hmSBb3EAC3$%?%$oq4{dRn5ji={aLwf1$OLQ)=K>CxY-Ho$;c>*-)cHGAI^;;~3h;D}kFCU=K z|5`(dw(k!+=)Dl1*%aqYq{?V5I$xZG{=gnp8_RawD<~qa#83=glq0P?4m2>U-^=_) zIT@e#!tBjG-FRGMA5N83}KzGm%$CTj~5K z{QT)QT$rAYvg4qD%D%sn(syRS zQtoI$IKBNb7M!vDE1eHlS_Vt~5@8;_Fclj5TfUB{roZ!}R@DnQP zw-3ufCB4);9iq+XIT1EA$=rtX^M2y;YOTP0SV)yyXB4C!z89m$lVIkoY?3}TH%C7H~c+I5+Lpzf<1N7q>uOe zF=*tx#Xe#O?qRk)Abg_}_nkW+`j)X3PgTV}p%jh*&lsa|TsRdrU+;fHiyepOnVvG) z-D418VMg3zLV|5k4L-U=0z{fi#CI9=!o^Sy+Tqe-bnhoWa^r7+_m}pMB%dY2CDZ3! zqnX2?`yhhY(MG`Bd8)Vl;a<%AS*BWKSc)7~VT@aLi~zH4a-DilBC5>UaA@4E!t_6V zFQtXsz}mB3W%FYR1M;WgA}dv7e`+_X?|K%vJEk6`IN$BHrt_84 zZwc?a%n1Va^tHbLjB-Jo9-VgF&F z4azgV{m|&4z|&9h*)I#ID0nxqc=n&bv~`$2i-PtMEZZ6z{;)xnodDI-u3?G8kUiV`ecsC4)r!r zp6aqG*-b=`XTRPy&~;$U<4AKVbqL?5HchklQGqd*$9a=n7?ucPQ!d51kaX9r zWPt`9II6lSu>7p?S z`=a7IXKiYbg&@h(v5kh}=?sqi`x9~ZjJ3V>lX~!Uh?%{!uMF!SI*w+jH-O2`G2=^I zREW)%Wn!931g_<}jf+KeUNe)ETG2_@U(bw_>}Pu6B@ee^8cyN+w&B2+4(B1pR7X>+ zvjgJ|CdD3BOoIET8%cJAcCfhc{PWZAWLUV_9FmgNjY3m1D$R6%wQIQKSnBNo^kO_$ z!q`)X$+O&--oGOQF)BNqWp4uvE?j$^TuS!=vgTdM3H|V7mCrZU?LC$^l;(y#=*7_Y z27)$u^t_zuaEr}pdd|Ew{r1Cagwv)Ak-kzS7_ZVZydXLS!J&FO^A-f)ovX@Zr}Kb| z+M68(xrXp~T0`qM?hf3n{fC^&I0P2Fsx-szy|Cs`o^i5m1p2)gKIBakFvwoL(&FkU z>O}eht>alhHl9GFIv563*|quT+f!`&`S~Mq2P!my8GJ01@UNkhn|q zYca0>djKt}0rOwANpNLl0;5z=<$^5@l*Wr!3zq49tDE0PIzvdX>)-ouN>D5OrASJ3 z2DhVhm0Zj-g(2)B$wqW4C15?z#$#!jURWBV{e66N41Y{>r25KCqqB0#+x>J-@q7B` z-dAHi=wtX??Nf0RUWm_KJz?Afvvq#m(ZR*A^3>g?Au1Jvf6Z00*7l;$FT0amGBr5l z@j3mL)BygwF?MyZx*H`X6b6I@M!>hE(0Po^RBDf9R=)qcEAzR>UG*oj95rS6v6 z58?%FL3Zt1`Dp+1)h79}dazs&(>@nKg^s&iAK6|}@Y`K+K8Ex!SP^1(dE96c?fRZN z@}B5`OGkQ^AC-2)e?j%LOL@Z}eLXT+wI&|!=YBMb7VSe`pU$!G>nT9^q`UV;H~~7` zOf59kh>%ywrRVLHgmHx*Num~L%sWO zs)28HIQ9*=u-@|Eu1&)YnFI{-YXX^>LKhb;gDFjSXlh!7}H~ z)dL;715Y00Nd)DKp3Q6a3Gm*gsQWRc21kl-sW=^&z$15h28P&E@Sd-B?fO79EH*Us zhO{*x%igP3ssckmYu~tt-<>vOF|pgQTp+@MO;-6^sNJCLFFVL$MDLC2TIXc2HDYg6 z(y?2EgE+Ze_oSaKy$9L)`EviS0easizu@F#3uMKqZ&S-m#|jpfvqyH)bBRij;WecD z_1!b(OHCta{J4g4^e72U> zMa^L7X+!Iybp2n#rN_Pb5ET#XJrJC;-VTpwoGix!8&U2LQF1Gti{(35GigZ;V)wT& zGbYO{+*ra6%3lJICnSE}U%ZK5e<=9r|2K>*G0P^_Kl{Kx@EyfTjDn8t_ncNgb)s1P zhr`@=2ry>q|KVuT04kM8t@6;u@sZag#}hhl%$GG2+sr$LzS1`eIWAK1I;*Y|F=YgF zGxZJf7@FXzXl3RQTQ5?q6VCs)M8xU%r<*uZ>+!KLO91bQJh&s9Br+!2ga2)Na`BvD zGsbccJrN8G289^kURpK%{0<9n(pxcb_}zI;0V(?LrKd|>{n7+Igpc1=KMzCLTuyB4 z11hl9?T9$WHUXCRc-SBN^gyOFTj?MBCbTb*GVS|-NCRBX*Tb~1E%kG9|B^_|^BrPM)DhKwK zz8)+$Cgb9Bqt)0e1Z4YJ6|KQQ0qURkY-Gzk=#v)-C|w%|0fI^fgCGU8GPLJE#!}IF zTA|6xCm*I4M7-x}M&ZD>TEjpcbqEx9dvQRs0nWYn(>@{6kCHvSUv6A%#8WFWp4o8| zV8uIkTW){~HBT;tYzm|2?@!gL&+$$px17(Jt)m3AW1`HMe(tApErvJkvt9I_$E~>q zJ1QmzyqljCq@ctB3tr~8`SA6tNivtr45XDL#jCuo0g}Uq^vEbkK+ z;vm(do{-B-iE{-?x(Nw;+R)HNYxj=O@Gb}~*n1>>+awCeblf`XPlmizCf~5ZT5vBM z92Wc50H%25$sYz1kixK=LK5kXm7{DAm-Cqff@O{a*xuTwM6KnEmPKyTKY|E)t6p=9fII+e?wzHSAfg z=NLXz&XIezwHF-k3(NG4=cC7fTz8^PD()SL{=6e>3@XafWnZ4|fRWA2B>CP!c)ok% ze9+}4NNk#HXBn!*zw>zvVb8iyx;Q;hj-P@?Bf3&;r|IuS;syVQEd=Zf-WJKb+e zNRqso8AgO3j$Q7$M`;-TUL)U(-fQ%_?csD(cpAqm*^QQ&+Hr!nwdK$EepHDO)s$HpzLk0V3>#+XA79Jw@hMs$=!+0 zJsrsX$#CY+J_
T?e#647$s_fE+_-Pqpy-g8$#9ezIH6JAG21rx2rIx|K3^VShe zdKVoJSA!JJP9M#{KYa7&>~~M1-<*tmA4?OKbYC_-a`eoQ7O~7-n_q(5 zC80fP_Vu8XaE|Do-hi>2UO(D8Lc|mQ>8MH4xo_*VAgO0J1#dOfB}ZQ@hxqc;sgPQuG2X{mU*8lp}g9=i;K1Pf~$nAPxL@~b$FBLnxe=2UoAHkFS z0D)E*Jle&!)jz(BXeu^_M$}m)wD{YAamNc`mlXTnm0zW4-1KijlEIqF(AI8RAN(M5uPDU^gjG zEvEA}-m4DVN$&>0jj3H#*_H}j5?4m69N*!wOLmg_Z-&8`ULFcK)_{&1M~C9KTme1n z#auQbJr_zhG}8;}#queU$mBC)_ysR{$uV@{{HakkzY}CEc$fCd#;*h(cPiZ1e?^7t zy7IbByU4Kh2jyLG9SPk!cS=jrb=n8RSH-$BSwMc5ar~hDI9f&Jh{rRJ;eR{iSi^-H zVOhKO=C4#E-?`ph zO-M(#fKA1J8wkkVS#O@8GJ>mdTA`1VhmrB0Sbkqg7w%!#D=a%74D$ua7t+3#Lfm|q zSQec#++6Cd-Wa68AV)XD2)%c^kdp4B7C=Ckr#6>o%G&Xk=<~W7tv1wmcUg7SA;2Ee zp{jBA8p!(7pQ~s~gaHomf>Fg%9-vBHAG1ZpXjIjV9nDD2o33JqB9_@2&-fw1Semn|aQi zYDnM3%k+ioCVZ)TNbb2>2Q;$+=E15qXwYoS-d{fh;;gm4cQxwal8AhDwP+3Ox=s32 z9n%lb1}tVZZF)i9?cBgums%_mU|LKoN`k=TH_tO~Gy%)8jaO=NR9I2nV%QXy0QXL8 zj{*}f6ezsFeWfr5oNQ$bWhjGKdh@+h?Fl02ozrIXuj|HR6NhqHBR=8Ie!l2e{$0Rw zo$dQt8yUp)rY){B^+0UMj=XI7PeAZ=oC_VGz{|neXZCUpc+W^b?a`|q$UdG^z1q=% zS51B^tM<|T3%k~gQPMaH3nlKD(-{Fd;>4}lqj?Z@Wu?Q`hMqzrIbt1Y5OxuJ-1m=zb!nC9x-kuYd728|R-1vu*pFrZ zZslUou;Icb-C%f^aPIMov33k1acuoFGmK5l*T}(7hq3#%^3m+i6#VI6qCWqR0MB$6 zsiF?kbpFu!cH1v9v?_?LMNW-@Px|I4#_d#kKlq*Yu50vuM@C7@smD}o`E*7#U*%k++d`Pe?n2l+^j{XCK1u1R-j^ z1`%6y)-}VDM_@idg~gZNn;g#$eZ}TWz{te!%*iG-^nIdRJM#NmK_cIlm%>p4oYNBt zk41=(N>zEqbh#5(?^tLmKktW?h%{{>u0go0XOgS1R0Ryr4p)>27viU(wOyvkG;p@e z+Y2>xop^S`vF&L;T2OkN8y!cG&0Dg=_L(sn1$tB!d3jASaD|;T$!1w(op6^wYpl%rv#uo7eJLIhlZLamqZr;4>~Mj6`fBtWSBwH{hpN+~pqm6QFrfFt0Xh z3jBDqZ!@e zC;7ew8gK8tB(#Hs4ZD(JfAiPFVE8LbDg8KX-|4yzA^=hG4%hD^07;Dy0{M1TbwPJ>AfqC_Dk0mKGcCgqNbWh_$c}= zPe+|&AwrDQiTE>o6da-^q4}3yH0YDiwR%(yHh-U2j-4SwBFS5*If)8yPTGk(YIJ~1 zqTDX@>VecPldi6Y-B@%!)|4%=4oAXujpAIV@xMAzTLSxMbg0xH*>j36SlD_}yta)Y zvADO@tT-8*a^JXA)O$g@%xw+p_8w66lC>T2Zv&?%Hxer8`)b-tr`X3cDj{w%<+yTg zH@dIdh(*!4eIJ9FK#Jc4W?YoGQ-3cDR@+E+k#E~@nm40pep@GOSx~ytz~2s$zGm0c z=^RPfO~N==u^XgR@11I#tVPB66pNjza~QO_$It1(mw(&UWKqWq0@Bf4xoE%z89)v0w=PP{a?@GXb=SDoGwo8N+vB z`HH*y>CdHzUtD!70hTJxZ|}QRhGRZ&7Jukf!_t%K*LyF}z~y@9$tb@8I7ty;5Zls< zorLSgg=|OE7J_G3*X!lkjaI}57~FmzOKdUhOBGb ze;0#G?D3w1T4l(1=yLk!{C+Hq9^Mdg$%ei9<1xuwTj5Cb`Bl!do%k{In4tanapc$G zOC1#&!z&io#`3KC;dCM2=;2l}6sHW$v7XC@UDIM6vh8D#VLWS;cX|MZRGLd){pm-c zR(6d+rZh|x5m~(7*-zisAdy@icYxh+L+~X>x-JT_ng5YZfrEwveJ+$*eA{rS@O3{8 z82ugORNsCApH`W*N*@w@U3XhyQ18GOy0sJCW&K$G{_WGB2C?|23!6&EA?=O-Jg*4>woEs5N&aX;{dHrpC0PnC+bkZp+g1;4g~JkR zg6UwFF?K{Xhm001(tAO{bv|mV`5RBqHzm`#b{v` z;J5d5Gn~15`$?J06ev%&sEE0DLgd?P9p|+Z;O2^uikC3~)27Z;1{uDWe8*PNeEPh+=zS== zjJ}6pqEctlTaU+TPP!yjjN=E^PqtO(yRqtnR=`2$PLQA}PD;}Eh-0eEZ``TM0S6E9 zs!-FnXyRh?H$Nr}A|Si;!T>#=WjPWx*H{l}X0u>=m4@p18&qGbJS57J5`Xj(u&!X2 zoK9~V{4z3H_SzAH2HPX7Khb+uroWq=i~m)R_5Yc zyZNry6K#-Y7m~nsYZTMm2ko|G_Q6i8R#TUhVfgv1Y}>x11iV}F?#j0hIXJo2oUUY+ zhl1avUStbV(ED)ElG;=QWXC5yYu2Np(w}zbu{jbb{L|wfp6bE`dWulWIe_x=Ka6>g z(V#Bg97nhI0b!riWI<*-{CoU2y?Xl~vWra^jromXIKw{Pu%-m$&$rwbOZP)t=IlLw zC%C}wlLt69avflY!(yNm8)BXE=hdd3UaUICH7qjhgDlsNbF2u3!1v4UUh+01lufz% zYyQIwGBVo{^iJl1|F$n4)z|60Fs%=(R*uy$drYvD4f4>a$m;pC)yH^bQ&k~sA)?|2 zQGBo@8Qiaw=PC-cLX&ZDVjJNL(v6rnX|fyHxw2B?iu#c+f2YS+wL!Q(k+`qx4}Bl} z5bvGa^t}P*$6vKFdJBM@JNnPRs|UO!s>72Ry)k>N>$!Gy0W7aNno^DtU^ZmD{Ke7S z|0y~Te=6KBj!RKSC8QG3Fe*w)N$D6-A;MQ#QE3rnq>Sv9q(X_ZXJm)$V_(~~_qbL@ z8I?rF@A>@&y>jn;p7WgZ{=5&2G#3YjvC=^O;i;@=odvMG{8VIHVIfRyD>=8D@VvZ7 z_G;59O>h@VEL^nC%oe?i~l*v1afKEhKn5zq-N z9v9^*MaUexcz3~XDh=50t_i<+{RJ6H7Y#ou)dA!0ciq=dwBnwVT2H-m+d)=yNKj;Z zHyn1K6XQ8MhS$S$=9b0EVLrV6=!=b&KsotiXVSiQl(?7E8LmKqU)PwgOp|+K^X88= zcBMloopNgWW=l8rR44T@U82GnKdqh81*s?}e>YR$RWdkDfO%X7!-s`0k`s~_j4>Y$c7tSgCrIJo^10vOq_KCy7P5C49quj5r8M)C6v-5wWPK>53U z>!(ws?ovL^H}hl&>CWhAPp-ZLpV@~oJDw6gp{(DDRlE%wYl~ctT+f9^bVFK6NmZD) zrD(sMDizJcofeuTf_D%GWa&% zu^j=G+PRo%!U0MCT!~K{s(^Ja19W3|Xi!q2+RQ_#247>T?TmW;aN08`d{K7*J$1(# zJ%YQ@yP)D!+Sy|C;cH?D?QTR1-lS_^M5AEW*hwYZm=@rjd;H}d1Icf@U-n=!tb$x= z7S03q*--hX>v>gg89E-!3Mo5=aENdHUus|%u$>Hh{1?)}$S0%Lg|7~GZgRHQ38SK{ z)uaDR3#b@+>z-K#wFLtU-))U~)JA?!JJc`xBp{>wtxKK#J(&2(A(cL(5hq@3F53CD z9j&{Mw=20=K!U481!W}&QoSw@-I7QF_N}d}uk$EaQs@0$^(Pkll^ZMo0`6U)lNZ;RTo+*QS5JkA3@x$It0 zmmh%HB(JkWO6hpzVOE<%Xfx!CX^Kj`?uF}XD<%=-yg27;k}IZD3gY~nSKP{4(Sx%o zM#3Nt$^tSS-JEma5^H4#C;8o6iheiq(z+UQ+}+bkr<&1v+|s$O_ycZ@ElK&t-wGRq z9X1}^Re>t64DX9QZpOhxSK+XpL44)J+%pW)huY!b=@HTT)w!Wu>uS)OuToa8^QMJ=11oL4Wg*q z^Y~Lvop^L>zO-vl7tn|9mD)$V7h!LCmpsmPf{V73l=x512P&9<|a5t7~2wPoBXaNttr7oTwjWD#nx6r6~ z2o1CMc^qU-M{Vw#RKs5lAlkRtMqM%uuRRa;x_-(HyqoXxhp@JT?aQ9BpOfXF6g#zUkPA|TzO+$7TE4?!*FTo)|UFBzWKXgB0 zn3OtC=8Hc2r*$O23|NS52^ZkR{W{yC3wm#pU0?AnNL+@UJNMqC@=ss`#=d(A>gx?$UA| z=D5myQcNH|iuS}0yDs!$)t6e=Oj8(Wx{P4&9)Mn%f%zq}SQU`mbUiL7Mx^m5) zcIp{-32eVmvLR=CIa-Ndm;CvIaJ_CXY>(f~L3T~uvFo3D;fP|B^hwoGkoj?g<2>n~ z-jd*W@?XXw?60BfUTNw8CMPExP~bnu{__>ceLn!2I`; z$MupNP>HA&S0^AV?FeNJXtm~9?GC6k zB@Pp#PS`ola_xw27uf3-)Z$IvP`(^{WJ1Q!Qae4v=^Z8>XLWGa=r}&(-{s=>ldhFj``~p6OL+R%? z6COrcljS1=1w5CuYoqJ3ad%Ogu$pQeX#S}77b2V*<$yhZ>JrHXw1c5|brm*-{f=<& z$^fm7i%hK^qi}U<(EoTG=~rG_IAg9{1Z8mQy{IM8CoyFE#dK?9@9BZ{m>&yYE)eY3T=wuM3abQbvIHgU|Zf`@?X+ z!C}u1h(hmAhtE~MDaAtjTPY*KF(@XFnSyOS*f!ZCR#O@Q|Dj!{>JH-FV$Ig$+4CK< z9dB9EdDOtb)~>y*nU%;EHs2mfI2O^j`&SFChv3NLrbM^5`M5pQ{n|69ez5LXGzjBw zM~$Mbw|<%rqr+CyGo=}bCgCepM?9L5Ra*Xp<%1D?7HTXbXx#>dThFajNA$r)9=>U> z5R#YJbC>kgxlM6`@fqHYEg#OLQP^1nO`SSX0yfXA@A6x zoHHt2XuQLV;S}Mv0x0noi%B%_I|r7wm4s6Zcy)w@(h2O;q4jo5opAdJuj#()0}xgA zO>-`=4gVS_?2Y_A0JmE03ROZ;oX-SZ2K&WJfB=) zLSysmdvh#o10usjKr@aXY%Z0i>IX!G{1LPS+f_l z8wOH!<3@2~;oIkq+nbPLr>333(gIomRQ}J515hpV-Dd7yG2zh!HwJSz!8J<`u3}%} z&6?nPZ))9-K2JXu6sl#Rwax{~Y7EI$2sQ5%?XAI*#iiQjL*GDrIlRDkoXnM%j#UOK8i*i$nQ1bqG4Qu2|olF&yze{jLPrp!a)PL?%CVP zr^klS-%xXUfUODhrUZ=|)Cy7Zm}hdSMm;bs-`V}-R6YEdF;1wp9mKZ-in(d-4cLAp z!Mf^IENnFo7o-;&#?tH3vZnM@%z91>UQhC&E{&^}$6pkpPU!RH5velV*L%vgF}D#v zg}fQ8I8uZ1IleXRLNyS$A>yKFG!3=tILjOzYp^^aKcIrVE2a8RHofL*MDx<514)*PPtNFx!3AIMZ~%pg4Os%XYr z!uLXnV;cukH@I)J*mLYj58QlH7bFZXyuF>p z{brw#vvXtOT#A@mRB^sKF$|sr= zZdFddo;sXL#f+eRX4Wshf$F91%+gWf)v+6E=h<8jEG*}bd_GeKIzt^X_XwXG$@;Tz z*Y{o!Z8ja19UnyRle9H=)q8JqY#T{)j(HWv(@fW6uRX_2#z13f$o#22qiLSk!{$z^({{mB&HSd|8=3r)wQa{Jz2o^B7b7YVHA?6_J_<t`f+4OG>)bg6*ka1PJ! zDpb^EJ&+ZDqYW)Jymu~<9AH#F*DN1<6r56t-z9J}3vj%bb5m_4+-Jx%wPJ0>L9Jqi zSFD}5oa_HeZr?CCKd&4#qV&R*v7QQ-#2lQJXZBdz+JGlc4^|8$_hPvrt8e|KEDT}y zES_tjqL(X0p^LQzz5na3R}Clv?uMGbW<$g9o(F$yG;fFO)dzo6eO*+k6g$-FfyH-KdyUmFXAHe;>${Me?cKCpY(v9yiUzhyrRFJDtG$9q?cI7A!1 zLc@5;>Ce$$kk?Py)gYu7RAQ-G&b8Gj#UXQfYF8(|yIoQdsxyeZn^OPYIw^rKf-gnP zw>5)P3e)T&3g4;EP9J_vx*L;gCQN| zN88zqE6Ee>EQ0;$JsG~_%s+r;XUz@_{_VnY$JYwes*Pas_lwl2=s~<-vF^qACJOR1 z2Kv&@6OT?$;mpflq%W1NWR<9i@bhw(&L5H|3Db^uXs9U0zhBzewv0!iVs+2+S7n3v zUee^APGc*^w1;$EbVx^4NlT%W@~@D5Y2*)o2H~UU{kbNJi*X_JWX!##Vc6i=idPNm zaVi*x#WP7iWam}oP~!0r_qS!b`iTnK{}vpa$oV)MC&MmE{9Y&OW%I2SsJL?Kkd-%Q zD|j7gjFde_a;N?Nfj5u6hwGk&*X8PnM@lQ_!vWIInjbx6>(ZWv;!6(lu4*lqT5yhk z#h@JzPOZL+G))Cx>1k#64{dM|f64^=w!yeb%eLI_eK7qhHQB6s7)Ani%|*0F;vKy$ z7xbCNfO%m|)ct%0EO9o^KeuckxgXA5UkOifk157LYNi1!l#BQ^derYXVsOySM9I~QEsyJl zaY=Wv#A;U}*#G)&F>P$nw0wbQJOw2 zdhN|HWGA8;i;Z9&9u?t628u4v(=-V_YiuEoi; zF)d#U8cIptm#d*i2;AN={62gT*w<7=pON>Bfg}4*-^qT+C<&5(oY)FZ1M9PnrIo|R zgn}DF+i4*7OI9msxCEj`T@xnB97$+L_Dz>rlG}gqQuQ21DK5>eiFE{5!7o$CZ%X}@ zD7k*DKajN=6D{m*mDuaiF^Xkgx|oWSej4{@iz%q9&Q{~`uNQaUa&_(QrlGLfb~>Gh z#9y$ZICsB)H7a+xbqgq0W6^+S&ko@lU|-BBv$Dskgyjf+q&Awor;HeJWrB%On zjY7c+5nGi#&auS5(D zyu(c<6xdw;UDJPGiG~Y}=dQ1`gN4Et;ZUPtl>5;$h8maHt3P_B5tbcxONYGaac!t ziy@Nabmb;m)SaW?{@>IL)k)I-5zBhA<3<7UsC@r7{GtPk>NOlwpd9CfCCn46su2g% zS_lsb>e?J_dE|ZalyTBUx18jt0(VPvvy_70&EdTj+C3OtXy?_N5(GJuTk z5v#Q?$IxLZEt!|86AzqqHKEh`hKnu3YYV0bpZ+Uxl5H=B*Aa?^{&)MZ@z^C1x!57t zzm-lvzCRn-3_jexM{+bvt{QcM-OeA!teWH7$d6BALjbd0v!f2ewwV!pjzWRyVoceLT|tNX4FU{AIAI% z&QA>xyQir-qO21cE3fz-O7BM{<}Jo=&$I#WqT2?C{!U1|$3}mlzX2S&MIYD@&LwWm z*FTW(L(@m3KI!aehe^NuQcvGbJgD-ZdExvZay(u%d~~!P?KeGmv|)EARNjAcr)D%4 zgTVb0S5PrB#;mDUMPx&h#iw<9mn-0g%J!sxBoFx7ogrNC))0Eg*QK?Kbz$`R%$-+$ zwBu4~pUJtOgD`4yBHgR~jbLk+ zx-9x?1fMTvGYRXC;ksF!)!W)-C>*7i66)H9v1^yd7KmTXSoU_sd)+21&^zvW)TbEU zJEzAT>gs@Jvjft74?ZB%_lE+mnO(S$)8iv>yB-s&c=`s2Cvtmi>&Mc66u1`)Q7ej% zVA+ihdW5^?z!?O~yv$J>lMCMiNM5ED_buIPdtO-GIW|tsZ;c ztVGV9qu!GFw@98|_|ue8I!*^pTN!84pjeSHf+Sp)I1U$@X4@u*e^cxKa$7on`KottuOJb?UhmIwWVa3pnG6?IE1l%}z&9aYW- zSy4WfYK~@H-%|A~Zc8^fEZ^Xj?NI~I^te^Qdd9SWbcE-)l&ch&8b<11kxh~{HdKgQn5k`M zs7FJNEKy7H{ng~@bv?0>1{Jq=Zr~mw@uX1|?^1C$2>B|#;!Y!;keDZ#9a@8MYQnsN|9&M%|A_WIs8s_- zH*43m)R#cz`D=+jzq_$WG*GkkX(;?L$g)zlX@vL#NxDi`^MK2E6XSX79+1o^7+Cj? zoD(G>+S*L5_=TR`;q_oM^!9(#`a_omqUziJu87B>LwJb%!uJjwqmCu`-XQnyp>Zzw-I{I0Zi*mnf}jYsXtp z14I|JYvJ5_yIT@d6sVGlD>;9t1a`@vwp%~&8NcWbJ~6f*Mz&plY(<|H;=+G118m|w zkTooz7ic(w;>9<$jt_hVukFr;MNw2Z#O><-R3I1_AAT|`AzWUHlomr@8V#-V7i5m_ zE`Y%a=echi>p?McGoQv_E98FD60xEu_wY%p>6z;pxM*d9JH)!NGcIsv-DN5sICuVu z1;0O3epR}xY0(688d`nqmedF&=wn=^_r{i%ZwecW$utmZ;WyPZ_~WH(gTXD@7S>BnlN z>Dg;H^OEqCM>J5#3P^oU&Wp#3ViJDQw-Zm{0<#^HrxGf09)p#uXD}Hz)N?x$&q3wI3fLP z+b^pji0@Ff*>!-_8%f(fq|ds+YnjRiXGSYfoj*)6h`#}cls|_rhPPs*PLTN$;r-qv z&szxyH{;&iwsoFkS0J}?;DU|j81U_g()>vJSGFs^&1|e@213KGVw|4>at9?A?=X!yaS1Pn$mz)wY;Qaw@)Kw7}&D(cm%VHTY1SymLL*42&;VW6yz1yCd}EzGAF+ zZ{3v!CH<>zYj;X9`4PPqn{pSbB+WQ1Emc6G_m-35-;==5C#2$jPcuesceiI$&w>NK zt5YR1vCwT3DIL_*h(f-Zbz<|Que*O8>c$?uU-7Twzaibs#IuEA3XF7TieFuc zLwQ@jnxD(vc(u*1_4nORcup+v4bSUw+~_U1!ZY~;cV;)*9&c~Q`#hEvLmyJ%;Bt2Q z(UnrdYev|ZJsJTWVuHMLvjyGj%YJ%0wW0yGH=N>{#@j5VzwJ23arkl60o{ORI4-e= z-r!^>j+lQBV@c`8)Yrl1SALW8=#cacE0+*BQWO{TijHt3p0mSto=sSK;338CtUc~} z7u3zJUWc5`+M`~-Xz21$P{8&~5}MAxd?Cg}z7OK^7lYsM??Mb$bctGCrys%~By__Pgfj%CjXzv;&-{mI&fWhFSEaISb+pb3tR z+MU?4QiybuaqSFirN~9uFW|6q3MFF=ezKVtz2(K(ej62@o^XoFAf68M#^@!EIpXs$nErV7Q7PR0eRX7!Ngu3xCdX}nqIeveh#`0JVPkeo*6mXCK2C&8(xFMTt_ z_tLO5r@$sig*T-WdMig8peSx&aPmYU$pMu2z6q~{Jf_nh zyCSf(b?OM^P&PVUOXt35Py#ZJ#=Fe<$lo_WakWK-9QO*1ahZ!hU5{ubrEzWih4 z`{S!9H`Pqv1?%7Za*%Z&g5f9qyeInVVc$L0PhYw_aE8lcuPgBlD4WOaKi&TsF2%du zZy%=!aG{{nQ zl6vP|j1g=5EuZPOLy4k?xG2jYN*Z#UDJvPkQo~=dVf0nV^d>jY`$h#YeYx+|^{)>! z`S^HW65iLj#^OPGVlwbJ)s(VIW#YX1u+1HZR6I4iEX*xa3U`X0md+@A!+!z3`E%Lz zc;#Tgx-F6g=&sQ0Q8ONnYE9KYBmNA6mcU<674o}xPkWWSM*5WU2h6YYa+TpxuI?@g z<~*FLH@uTZslknb$uFD=i=e!Krq1U_c;iUUgi}oU*mAaGRUqsOu-xMBS8R($%bfE= zHt%R~RNTsE_|5=qGkyKFM4kq>?*+Bo)}}$Z{y$C4k3*m`;ty}bhasWv!^%$Q&ydC^ zq1XFs5`>iESH}rQa(h|yiW!-=U$uHXb4#-s*|Y?wuJSeGctY9BgXYAyaa)|yf6N*) z>Q00tiZ;XElp56n!LMMvslZg+kBZOx2QJp^rGnY~!@<6ZI2^sMe>;@Y2=1-yb7z_p z!Fj>s_}9`-2s_gCm&=Oq6E7{E(35@2RhP;4`RcfU?~){^H?m`U~7recYWj7sCEf7+gsIRqS~nQeYmVF9Y=OS2IIAF@Hca2J zS&`{)DaC&~VeKVGLiJImoogQK(WwXbkxL$CCGt`=A~Xg#zsw;B2ym9H_T#K6(R%s*HY z>rndkd89_QLBY?{>ld27!=?SMhDrs4aR2Nbl~VO&Tn!tU+T~XRNi%iNPfZJK+#)`?RCQn!eK3SL{tt#rQ_f|7@O z{CPQQ;r!2?y2cxuap-SL4+mod)I3!crLv{rb@9sj{X8{rsG^p6S4blo{+Rn>UpkJ7 z%uf_pNq9n&1--;zf7aKg975XA1o`adrm9t$*_87}#o$p_13HMVe_W=;Z_@GxoM)Pzk%k1!_5_C`?m-eh;heo?{;vjS;1H2pJY(4(uuM!k-2ZAc(%lgOXH+(dG2n@;4ohM;cLhp zL4icRez|$4LR2U#JDXrtj5QZyHIi3IAN;E8h9K2mcuA#neZ5b3_UM7{F|t`W=mH!2 zb0~OOZZ8o>Phi;sOj$bMjJx;?XmlxjX9w}j_$shta7l1>XS)2g)ko=*S$pMpB*Z_tzHkS@P&@~u8YtL z!F!^k#)SvaO)1ogImekbZ{T(p9PU zBIukLYxt*3?!`~Rez)jaK_%IS!a@2|f0i%ZT~|(gq&($AE_O8BS8Q%2YTAP;-3pBI zJ++|rb>jhdvajrVnudPZ|MvwLulpYMq7DA0G;;@iYsEB$q2D_slE8V-KA!aP7GP?U znmf}Q4d%}8r}(}R4z<>7{?4Wb^j=Zd+#8aObyOEM1>z&u>6Xj#94Us>{m(hg9<;z7 ze?c>~(h@xIUtWwHssyqK(oMgH} ze9`*DY4-`&L)EhNky+_MTf^1|pVkmE)-|kfXBOkz7RH?)Uqqww=-AOeIW$n?_$Huj zT?@0mJHB5O>cn&Z@$L#1Y{H2j2HU9o#GiXj^Jq|`w96c8ra8_C|VAG~PICN3} z#y9y+_#EfYal5?(jjv#Uy#U##llqmtgC!Hs%?oLLAbT+SuQ~W~UN3>T_38EPr(1!` z=Blm#VhWCxykQUt&B1pam77bR=78#%*j2^S7EG2q{oztlH4NM{{?;0rg{S!ZR&%YY zaBZxMC62r|+}+=9wpaTM=8SK?2ok&z)Y5FkTKD_qVUQqiY z@o-eG-PwzS5UCxP_2%+7I6TtHz9*~+Y7Ji%wYpJ|GuPZ`uR${&Xt&oa5FEi6YBlw# zR1H?09QWi{Zo%!$TjORtx{+b>nd><5*96Bn@7dW{g&}tYt#zLD!E-jg3Z^@)z%$!< z#)N(h3snMhO_H0jZkFfzjT2>XAuGBdkK{x(X!qZm5$|-kjf9i@gC5v2!=vb3+5=#q?bayM3oTc-o67Si> z;@6dpY<;+gb=vHyM+fXYlR8lx*a~%TUfkPZ)e7=2GuFekaxD6|&FTF9aXc~BA-d#_MJ{zf|FO+2YfI znf>r-_q&m2-aW`TdA{h)kv=>jVq&WxG5{MVl@=R5P;maYohGfc2A*8m2EJD^QAe<~ zC@_SaTQ}MR9MZesWz^t~CnWcDLp_Niyot=~cB#5Cmlgwk{AN68P>KrwoVGaIv|;d5 zmv>@|6Y#;*#w*e+1=nWi4funqKr%8hWYU=iKGY={F8yenx2Cneaw`EH;|5XAW#U6H z+RNgv)kof=Ke9hR&jnXQrr#$WDR`)JgvR*y3*P;t6D)*p|c#^W+)(#X?(iB;d>HwG?CfAou8x-$kMz^0(U+yQC+J&*Ot9Jn?;>tz0TBhD`@Fxs&gZrmxNQy1%jS0+^p>3qd7wQLw$7Bb8^SHHd19W{W(j$m-iev+HldFB5-?ijUOdzu8@a+7)?lJ9@V3qHHs!M@`Pt zjyA!bt+JdmVPyT#G*#ZSt`i?AiCwUo=p^%v(3d&y)8JH@Kzg`U0Z0hEX-Rv~iS+iQ zB<^nl>*oF&Hwd3uGvThJSKors!uP|}H`inPi?89$SNd?xk#389V;OE}NN>3Mw-4E0 zh1Awv9Ycv%inp5w%kh@}s`37J)u0`J<4?KSFdDM{(>$Lvgx${KRN>VT{Qb_#vZ8?O zX)uxsiaO8+XO40?tz7H_A>*cO5rIBfe$eivb~hPCDkT~B7~~&2b3dsXn-=pc?3(ma6|_YCz^L-n5T|CuV;E8&!TbV}y+ASG|;8@HDoS zD_Z{q=Yq^H*xVrf)9vw!m#v7GO>wQOdTIn01=Cm0byeY=rS%z|C2gR$+Ocphtr|Zm z6qvJ2mm%}v>@q$|IV#HWa>WUkLC|n(>VelZY<>Jqyla0XuAF~Ks}~^qU*^o6JXh=C zV~nvo+buG8<;&l#`LPaM{9dQ7yV8T=(zmSQtE(WyhrMk~p##{`cfKllR0)6N{xmy& z{{(kv8fR9Kc&+m?JI4xg(OxBma$yt;H*Qbd#9ai$Q3*PR2X`8!(;AIWF|Fo#Y|U@s`pxLTI?- zOQF*pNH55nn9k9S`*tyzz3J=6yn6@b9{UbsQj$XxcW*!X-#F@$Rq+)LZc5HkZ6WX3 z7U`2Eit*5yzjRYsv=}@O$gwUTCvzw-y}0dP6Y`|Ke0~o$k&SeB7luZt63LStTcIZ`^NywXX_Srp7Aq*mBYPov+?P8Ha4X z*57hSank8K-jI&Hl?oag)bh|TSos-yVHPZJd>VXwIuln`X^uA>x^Q~G@X_;h&v7wl zbjywOAvD{O^ep=1AjUjQy8d{TaJ-wMq@+z~81&hoAVn_?I47AHD4HF(N$hHVQG7bM z*agkscW%Vmn_;1mI-S6^!!GT~h9QhtYOHsy|B53Qo?LDEIEa<^sPDb&T5wcF!i|~q zPc8@XZ_zrQ2Eh}qH%7iyqKo<~p0$Q%xRLv5;onno9-SB0-uE>cn@dNRc-)9zRd#>l z=iOuv(hsM&#HSQ2wcb0XaH9YMbz_aTpCWryrbE76Qmg?P>XV`KVcj_A-Ly7WPJ=q` zjsq4CY9W2kMJG3lVstOrEpL;_ZjC1*f)8IX#DO4OUfLoCRq6=E~NXnN(bS61n-=ffABSe;Rh4 z?B^`eTM|%Z&A}~+MK2Wg_Tk8xgC0+bhvJmpLn&US7Q8vs@}h{$b5~5b76rTqz%te# z$M|^~sx)1Y>@pvPEo>jx#+GTocKYQZX2n^ci*DMvo#fnn`sZcRxs#yCSAOk^(>D<9 z@7?~A%!ycLf5?avuU2QSOn=hvQmk+cKj;+SOy+nDTbOodq3k(IOl|oOSRV0s8gqCW ztRzx@?PMjtqqm=DpR{H}#dK2r8?tvX&R==Jlz92NdIat%65c4+W#6y~tqI=3;h46S zSlGXBIaRur^p8CrUS|)h0Qc)vI{LeMaAS*I@dcwnn2o;d#$`?B=o^Z~JZ;)w?B{X* zeBveW`LL1yXjlU(G=} z^DM;A5_xVYoSwMY4$}9+E+5EmgV;}$g&U(CxUzdy;^WCikoxl=T5Ns@=q>bfM@IUw zhwi#lLuV6e`xwYji%33wf_sHR>NVazUP;~Z21lanIAr6`7r=nBY$1Y4r;~=8wQ4a zlZJ6i+}m95cVA$LGwr6n#2b{*ymowXG#{6_dwitwdtpAt)QmxF5U!X+x+f4WLrqOA z`j$&N&i4u)IkR^NWQT_to=CKzKgFr`*?d2?rFqRcH#|qri2AZ%Sw!Bn2cc(Pbpfo_ z>Un(W2G!G7%Ah42()I6E$nGfa2{^MHUe=nQRR`T<|TNzpCbkXc}>g`I< zDB$>!;5~-%1%+$-A9h28SorTc!r!qoZN=rCLqO1}_D=aeFgI%zlQd|C!>?5nTOa0N z&UNv#Lid}2-mCpe$?kZR=(yxLG&2Z%-*|!wI{QGlP}GXzOSrlBr$p$9Z?&84pVwD| zdg341)Bb=t1E>$frkP%}z=gklQZd~n;Ja@3TkDMp_%;8&&;LE6Ro!Z?JtrXCqJ0&TkO!7A^ z!g~vR2QaR|Q?{8UoABXg5nk2(B)@P`>+&m-*QnpMGO?)?98?`@wMvU|N4duX?g%Pw z)Z+ca+ZPI6!laD#PC`ST8H$@#y9x8J#`xZ1ef`{OQdhk#(eO}%qPIrP;JC3Lv;T%rIh^(o`$o{BW_Qt&*B9K7y@`v~O2L+y=9gIy z8(qeE$OI-T;y*t;t8Ln+@ za*lEChOHN#pK8B23OXl~uIwOtO6aDP^yT7U>drH)i#VJIpF56C93=J1 zt52I5zkMGhb;JR);O{vw_WBgpQOzbW`FSO|g76OZxg1ncr&_Srxb)nL{{^VbRG+yr zR00|oxgJXrUsok_pSi%u6vpWaUEDYS4cXP4ADbR}0b9)GHhea0#@ym{EN*9CU`4(c z!|un!D6O8=&74*bG`o|$ZJra5G`!3I)7v(rjI7(>i^Y%IH*k$G~L(BQbU-f#OJr^?Ovs@c2IbA9Rq>dY6ccG{EmC@l|q%m0?W zQfY@n>l|$YiI>48SV&ez?gLKU5VF$MEyvR#&%=UdE7AW6GvAZpVPIyk_YAhoK*kTp zFYcm}{6iYOFXehY@c{00c%#rsJa_|@jFdFkB`0;}!V4;F`{3}Wh3-B4`(wypo)iyy zCP!l*pQ!*d`<&+2Ka1hyN~q4k7sU`T&fCIHUk*ISZ}Ce-R-*YJ4`+=Olx+~l>{jF#^MxPl-_ORz)BmE;ilU60PScLju!`4&^>QxR4=in|ZRj(EJA?;! zEX|WW%`Fa-l=D=$rq$o-l}qOPYH^RIN!~S0TF2_z8?xNi1RP?nFdt*fU4&v8e)hen`fG02^&7Js>|I!U+AH#B$BRktL zIioh@v|U=|#+TmbIji`%!H+erxcJtJv zfs*z5C*Ci!@s4#|>3Ub<_4v+Jq7a*j;wIT^c}|a@(PiZ}2g_7ErxEIb?h*oL3G!kUWW`%!bwWZi1OAgBy=bKW#a!*sR9F1M*BkbNeRkfYXt z4=lN5@=sU5o*kdkuV3zl*xTVA&su9xQKa;td@9MOG%zi^`bP5ib`N8DZgt~p`<6#T z3B{Pc<+L)}>k(Y_l#gNN8o?U|2aWPVzM|*KjDlWyKcHva^?<4jygu9&ula}U?Y_8j z%hUTb1VVZr2ML{->kyVCRhFG4F^H7_kR?fhd&i= z8;4txRFYJr%t9p;DdbivghGi(Qz?stM8-4iO9jrn^FzwCxTiqH9t0&B7Ll2*91L@5YmgamcycEY_wlP}L6 zNXKu>a^jS|Iry;c{hZaz7c^5&_twy@!sHiRL93|)gqJ%ot-Y-gru2_c7oJpNy8Ke0 z`5PL1Gkm4kLh?b*vI@;dx@z%rO2dD&(0exiFjsCZ2PaSfaD-~eLz_@+*J6h0-UeUxYMVa z;hRpaV_Z@M-liYkzLQ4B!=C#cz3nTIOMgBuk>mxpJYQ029qxl4t95Q|6D8cs@XX+- zCxZ}qJ$^;Oxek3+Jqx-2vKEmgyyWgI9i1ZibN!TZ@awmCsxj=H=qvD_lcX-;uCmOC zS}tayv%wQ}ZMhL>Zp*#Tymk;Oq%$_~zN10Na8Y&z$sfmcezCu2ONV@M-e*>NG=P^{ znXZ-1Sgxv-9XXm0q9X&bZ#8KU7)4un-b{hdiSC+WpD4ik^=a>ajU?~LZS`V9An6mV ze<>LEoA8|^!zS*V=YW;*$A%NO&G=hOEI4?7CIqNjF;0{H-@f7}iEqTpaLDuOf_Ql` zR!?LS?PD~aI36t9xhovDct86SO8Sa~_HX%4knE$C9E*SU)I!D1;LyYWYT?jF5t%xl zHpsDbp>EtyI0a!ez5BV5sM6f`X>W1|9Dn!0-$a6V9z0Cf8+3k#?eIRe#V!_|OTOiL ztZ4$LfzJ1z=hGlKd@J(>CI)Vc;4nN!J`d-wUeuRVCiBoOBVO%jNQ&eHwEhhlWSD>}~-RR{_}ynQ!`NBzZ8(p!@u{rsl?E2;a9ulezgLa>TVr%^Y<@j{EsM!^(7W4yWb* zVjV#pdeb_I3%&UBA)cYVZN^Ead%Fy?-$8?QXM?0#}*gn7p@oneMZ0N`5zil}se*Zvk>ND}~hvO)6{k)--jGFd*#I2yU^ZSyw*9KynBxSuzWSz1d7{!Df%k6gG-ud+CXg+oXAjq zG7>Zd5M0}0x@`a!qYWQ~kUaH=Cw$Z*nHprf=`H)|U@IDOtPM&K=*5a18A$>pk8-V@ zdG*1#6by-g@_8wH%Jvce zeDlX+YL|xK!qdpFL3tslZngblz>_+>C)ekEk?aR&{BIq2Mf#Qqu zkl%|L#f&i1Y$~vZKuGbb?;!W)WSf2s1761Nk@$3{46nxieO*X=kE`9Hs@q7Od3ODx zMD$h)O6AFfr;_~L*qimET1w^6xO`JUWvdOG$Pv>uS{i^eIZw0)o9W5RW4?O z_}|lMYy<6hr!ne-Jb9O!sJuArw}(RZNZqwZiIySvwa_=q7mn(IfNj>&}mEAF-omS!+L5$9tS$bi^;v#avSyXMj(&spBB9k>|9 zBUxKg0#OMA4VvWK3KlC@9_uCddtE{4OM?Zt5-35@<|RD0`R;SEMK$oXeEC{$P#m}| zkBs#7mEZ&Id){NuGT{eps}8-D247mO(uFrQLZAB7UC|qUkeS6NrN>ulmI0qy)NChE3mvDH4k8zmk*gkR8r?h1Y1!nS~#anJR9cI!-qYp zyD*jYr0B7bUNF5)H4!=&hC70~14HOF$g<9BSgnopv)yL42y_=h(#1q)KH_=YbG`0& zyb2Z9atfjt0Nxk$qOgjzV&2)#!-970Xk40o*)+a*9@{`b#|VMAbEvM*eBUm|Mf?~q|9y*7ipm=J1_4u@HuH@S*+z|+mgW1|Q+ zKHGx3FfW&ISEI%6gpZEGxz7r&0)ljicb0s){Hh6Sd~(#sEC;|KYEXRHcL;bm%@bnk zBEhtu(X80Dpv7l?lCM4Lq3cC+FRua8j4Sy?woS~v5>3W zhR#)ZHp$hUmr6%VC55rCWDoW1>1(s#^W~6}SI#cMo{Mcep0o_|*8!{wv$PDa3=xsjnTG+k!R9HhW{U9O1lX@x`42O?cd=UE@M=2A;Y7>&F># z|4{S~D$uNBz!K|)B=rR1bv&k7LOa=ke}0~2Z+e#qH!Z*EG26GHhd;aB$$iAHH2wT% z-PTeRl4ULV=0AWAL9ch-n`ptk+cg5lXA)85Y>jT{3gJPxM%Zl)=t1UV8ca7f5^fh` zzmZ{nAvE?>ob5Q0j9qt%zf{e(BcEcJ+Q%!IsPk*L&W*S>+Mk&&Nrq) z(U-;G{nZJOFw7+;Mq3%_a^an`^o5jN`^+`XV7d_eM0?hyReOiDFA-vfp@22UI# z($JB;)g`Q^5Xb#Nf1`05a@c+@O)gqa{zCZHk?`(?8e73E1Fld^0AOPVQU(%H}dE2HTM13jE}38 z^|oY!{znye$NR9=ux~p|23Ov zJoxCVYEyd!9DkY|8N7A?f7>k;pZ(klR}Q;1IPtf@zdfuOIoefts+s5VR0P?dKQdi0 zB-|x$+B-h+xngWK5UUcO8bGbN@Z(;gouIi|Wmsgmh~xos6e?MJP-exgjOs%HwQ~XW zA#Z}9P`pg>iQo|083*`_tePPE?Bh$GS=kUGuM{HNR)}v~!}kv#%)}qul+EU^lc2ru zvV+P(1#)*}n%pcUbDvjIzm47vKr+K+kbW1T+ z&(JWePIZ^LJeiwsj?8B=Z@~gCyJCUS9JuDWTk3;GJA6H|?fHHo;(4LO1@MYDA?4pC zvjJ@eD0FrgiL7Nn#5bX+eO6__o#XfCJaZea)9P3+I7j-OGVz^m5$P~L^J7(+W-{q7 zxC~x*>cO0m!G7P+ZU}lv*_LZgdTb~2wU-4bc-}-%`1JWod_RAHDY=CRnpz(yPwG}c zMxZoj5tRmBdK6jjfBtyqc9P3~d?gTc^kAC%%Oq^hcye~};zuZ=TPc^75ney< z-7$96=HJ0&?z#DoOK^8<8?gUN`u>qA6Rgj=$#RjLN7)08YZy$0mx+?krc-H<>*Xty z?$HP<-eT5OWNshALs#=%B=71P>pNTjBRNm4358QikHJ);ktJUG2@bx$SNWKk0;Rds z-WOY{VQ;3JufbF&oM)e-Pkv6oqxzXLcZyrkGqZm8G5KB$zklf3$uci!dOb5AQcA%& zPmMmE{a-M7*(5glawn?fcHNt2E``eR|HKx*l%uP(!>$(QDlBQE&wTySjOIEQON{wC z;Ydn&_{^Cv=&)|0+N`U?-#1u7-Udq?*6G3vd)9ne?a=u&@{_@5=_jIP}dh2=x$^2|AOTy(kW zK=EqFf=bol;7^1Tpd);XwbKobvwK~dB_597?A;Fc%@Xj*JMiRR*99Mk7hb-m5FSpo z#egk;5mFf6>n+r1_(IB1CxTu8IyP_XCDI7zlchBCwqPFAx32p>_K^-**1l$6Hm1Sd zf~8!ylNs1iq$WPX+JP%cHVR)pbRbp^ICaRA@5kh6H3RKDvN!%$djjaND!Rm!&9)3Y zb514o71AMX(N3F_o&tV5)+u(7-^cNurPbF>(y{Z}Wa#!cnV22EvUwz*fhsBLet!Gv z;k8_WSl~?>aNU14C6bnm&EpRY(;2N;65i)JLHY&1Z8w@nyOBPP6K@uuTo>rXCq(2u zY=;Fy)Bj|SM8JjPj8NUmYE1m%{p;Oo!p*ob_T(CwkDN2U5+a(G3mr4RIU^P7!B%_C zs^6?Mc;gkv{V^mCjFL3>eyAZFs+nQUA0}PcY1Xn=$%1&KQs#co<&g7|$2`w6fClGF zD(zFt)9}qNJYsdN6<=@;@-02chc|NpyKj80KtZaEn4%jUq>XZ1SVD@yu+DhM>QWo{ zFl({6PIsb40JreF%3^$b^uN}D!g{#w|BSi8ig2!Cbfr#TFNF$`(}@J9)R#Oj|OrqNJ6OPv}i zNA|>nd%kZt$iTv8yggdkgxn9B!`9c?W0vIa&bP#8vtfws<3F!1h@3r{uPQ}yqKDso z|EAZ1kCo58U*N6-O3TE7K63`X!^!p~;cg5TmtygcX-3C{r&aus`LLV2=fM_oe&6`R zswd`6e0s_G4bKATuz~3X)NCZV>39j5r}-3oIK}XbC@+KIAEW(RD}88PV4i3_(}QBt zS~oimQDL{jtp%U^9ay*B?wSTB;fbslU+?r1{=<&2i^u-YTS|Xf|7U##PWe&2-VL>3 z-gi;=T)%$guE_M`srDvb5?i@XVHr@Xay!?2xerb%GILP!==i86c{~3o6?|o~Uhla~ zg?!2EQFMH-H<%>Rw8|XL=p4J+`KTY;Q)X9{9%10KsIXtt0_iw$*sc7@ zEjsE)o79?pN`dMc<}Ce3)fhSGe^kMY^nmTpB?cIGfZCebHmTbU$mtb0ny&mGo?O>y zQD<92<^lV7>4#kKk$A+tD7jBq!KNzp_zBq`(hn~ExJ?0%bydygbA6tGmh6Tw1wDo1EHyTbm~U@fOkqOeyV2Hfg4Nb9wQ@*PaN{v;3o z@vlzzar-=6DB($>$#kHjXFojJ0aios~l5i69TK6+HpcIc%<>@L*&|^`1b7A zS~T%Xuo0f`K=HML_cE^4!(Wk{iD#rY(6wdb2YPce7;+syB~0$6Z$_7NQ){|0+07{R ze0mu;84HxZ7fgoVGajPn*Hl6CZaUYay`5NkJXo|LtP0FM%wBTcW8jo^g4vmv0+fFF zVVg-tJ^l<7W=eQcj(c|Q`0wW4W?b^>d194afyK8E)OR*FqovCFR!+fEd|#_3|Gb}g zkY24ll$guFeeLfHI0%p6Sipz-yUBg#zG3ias#q@^s`@aK_8=QRzjD9Md%hDk`-!I9 z?;~EDfT)(qQ-sr;nfgCA>G-x?@KzIXASoP()XUfK8CAbGc{}9R1>SFQhXU z3nLmj)(~Iq!*3xEuNl8T`3*dvcW zX;qLBEgQPKqycy4OnyGIQVzFVtyO02JIUV2;8#t5Gk83Hc>d^tLHNa9YIq{_6)fbW za((z&gLeP4)StytkjB%LF7=tb&*k^ZOJ1hngKCL49}-%TarJk>)Jz3_{`@HL7Iy)r zuQt?qkVSYbdD^#*cQ=E*&RQ3%vxB53a(V4G=_Uwp+xF!Q**j`yn7?u-^Qjpv8y>-i zQQZFPfllAnA*^~d-5p|H1A=+l?z_IVqDM*pFR-h{lBylAnNQJy{kKX^-G?5ay)!vz zcdCcvnm({!Q_qG7R_@JS(|Zg*+ZA*mUsc*s<@GiA_+0IlIkLwp z)e*Q9TJjZ+ojTFbSR9Dvr`&Zm-OmKWYflfX(kOzjY`bRvoh`<76TufgME1dU53@k= zg+|EU**H7#wiQJbQaJPr8{kanrCV1l>QUgk*!5_hPOMwmZ5v8BVv9VXCbhf1fZTaW zllyNHkgw6kdywR3hsumS-Kt4`-Ky)wt^8I{)@S~7$({-|d|D^HcYjBYnq<3mJ`HGl zsW5+>qYu9pQ$@6eDzU$hCSu@1Jf9kZf(OQkZ}%6w?w*Pid~?Qj-i^G+6F!ZNSZI~w zIm4Awkrd*ezb=vfX~qX+>OQ&a)|TSv8?QrLIEJTuY&`{^*1^D{y~O0sWUvU>QlX*Q z0)zL)D)SBvpvV~`epbR!99pzJF7cOew>JHIqxXUAAELATj9)jy$f7{stZyP_vr6lW zo6&IVK^A#srb@KMgCD-i6vB!40_9)NNzY%Tss<<%#0d1y3ei=TMxPWYo5qJ+u!AklHO!o8)R8m`nnFHXg#cTCWPlq?GEg_Ur+Mb zo38nhcZ)jngT@~RD!{n#$g?0S4Ug>KG0xG{jo~uCK89V&f$_*yJE?mq_$@bj{f1o> zFz{@)Ey%4$w@BvRwIsjt@4i7ynR*xAJ*vWz_@asMM)!@Jl%%2R;q$du4cqWf^HqzY z$_%j8EEv)JPyp*Ao+=)BPPlev;{^|M>Orx^>V;f=8J;h+nACnjJkSq!wQmpT1m@6H zDsuEXxV(1QR*rBrR{!B~uks^2dZo{U@dbe(@qyYf!Jb7>?&GGtyINpX=yOqZrUCR6 z*XLWz4YlFig) zvfciI0k)%7|5g(vyf%bflZGO7F=>D#V@y|VSYc!t38&z zCHRN%m0e6OW$esDhmP^jl}i-(ez#uy)A4p3m%ztTtr;l(tVTfSW-~6NZ&RKhE~gA=zd4ZTwPnb9XO9cYDy*w-tlhdr6g~^K=kR=6WVp9E?pz znY6a4_ki+O`vCi^nfQ}MMo;)cG3c7PzT03s0!2TC6{?kqzx!VsDD}6Xv6uDCi1FtBH0HZS|qk%K&l_u1b8P`f{Wot+o|{F&bjEj)JYp1ZO64tm4aca5pZtZ39FJ) zL^$**>nGt6aT;2l@R(~tf!U6%of8qRstu>oaEIt z+VEVb@~Ubwm-_TgvEpNV3eqOdCbZZPUu$KwbFqCl9Q~7K&fHQ2`Eq;S_;yin4~<*r z*>W+46;#EDN{~L!BLV8oh-UbyA*C#|SOVI!f{#>u=rB9Y^D--`1XDahRyal?L8bPh zSSADUL-cCta@H=qe>lj*YkMQ^FtvZNDk&YFUisTA+fj;4GxKAWNljodc2}ZTv=x7J zty^ct&VcP}Eq|B1r{G@`rqz19DIm2ec(80o5$qLUSs5nq-|ID8llNkx!AAdaW9s8> z;2*8%NiZpuUreMidIuwdX&R{7FO_6Dh$)ZJ?v!*JwL(v6Fx zZ}Xe)@Ls2rO=wzuUGyO7^{bgKjW6!|j?cD5)O!bg$L+hf>pdm8M`vYQlSmypY@!s@ zC?+>R#U4Mm`SM=qT+H~}>QfDK?>1U-k~yEMqYGoftPU^LneDK=*^bkcJO)2KcM*EqHQ{XEtO zGZtAVeTIq0$l;oL8x(~) z!_fEFRS(OT&G5y+vb1@w3R*1=cqI5U;D}}(Um%s_2*1~!bFs+ASLcG0^v||qOzY|w z-i!VaWaSbZO7{6$#V3>xJ|*uXJ&r3zA$~9+cg5(gVl9mDkJ}~hkH2W_`%g}*!h?fqNV0vw_T%-v7a!IevejNgjc@Ly6xcJ{3iCyqIgo zF6!n^!(cz!|suAh5VJy1D7kb+G3*HVKHBD14#fE^O z9~CzJxM>&9GW!G#BoFWSwtb$0nLh48_X&TQ^IQJ*i@rUO{c!K{?Qj~Hx3PHPwS z(gr^SwpJGb-L@{Y*fz!3TQ!JsTjm92_Y0!qn@8>sioCGy=E;Ohi9>MuZNq3a$J{90F*~rp?DUz!) z{JV-!_SMm*D1~qMjb&(`5j_uGxpzsvdgupi*`+Zt-o#r^-|=(Amv|LpgpSeoc3|lC zTJGbgsMwU%P?9v?4vd{QKeFyY4vf(;_F zMH6W#tnh3wInx6;e8Myf{&2w8c6<_$;x&Iuf0QnS4)p`!CxZ#7Xa?OxJhaLOhPMB&lC2 zYr~ol>DgD)xln4eN%7_tDwcZixIZdG*nWTUbI_4;SW9g(bw5CSY_;RRR@Mcge3N#~ z`ETE_W4h^77}$3wjmG2IPP2%<7@)Qj00&&<^7O;yF5Ewz83ve zZ@2B0DZ(`+m5)xj4RD^n+yZNlzgRs*EykoT(ym^rrLb-HMMbYBDp<5t zbXdKtKws^IB4*xFOl^6UIbt;e=}I=NfTAYcJOtsGrXnP3GW63Z?3;xOne;N{1q~_ zz_X=B&n31Go|LXlWLIp#wsVK02SQ2Syo=F;1rm4g1znYxKRgvU_9Rj8BPg&|s!krQokxaA`EAh6aVR~3`` zgc$?0_}>cBVp>5DXfLBK5*|TW!U135W+>5QZdDVigRKS!e`uU22c|)XGVz&o%s7@P z`2G117R4P8a9PaM9(fP;AE=awN@9hAJc#)5_xSqE?R%~heq8k>LiM=7APcypbz z=T~^PcP`-(`MPyOcgMvhQz4K=tw@dRRhQ0w<6Iu7#w~{_OQTbt!NzXdRj<7oZj^W& zv}PjwviRiLUcx<_s2ovS71o5a`|j|a-PDcJEj?1FS}3^Tru^RxS4rM%&vw1AH4OAi zTmBJC&O=S#v69(4~N0+OyJwuwq3ul3uL-V6RSlE@#t$#*Z2(-h;jW-=#pC- zaHdiMg_w%r@l%5ov3E59Uq=t^wPqk?mQC{AUz$*V7|{$=)MaS|_on z4>iBuWcj91`6kHw>E`so z8I?Z#w1IinwZaWw78y-zMyA5%gy>DjzqX@bwb^rv%q-#+-?R8cAPXn!_%w2k)RG?T z=I9H(gmVghl9lm|SR(yHB>83|I-dXiTR*=EoI8BtWb=sU z?ZLF%!N2ct{MEV#zOD%Rv*+GMTq(z`URRBONK#>{YR zVM8mHpKCC&;KOyJpA@LNx-Rfzo*i;)4Y598DaVb;JNQ({_v=YT$fXxtgRo!I__AGY zKb#ab^RU@92ucfU%y?B9Pl?Zz-cc zODA4wtq}cohbH(vta`0U+8)myuHiD1?19&2T{K_v`M%;n)BjQ0f;tJ&IdVQ^Z%f~j zm2)8rMTPfV6Sz!rw|`aNy^O4cpw+wOZRRP^?92RyL#G*sYfZIt{X4Mvwc1=sQw2U6 ze5-Ogy%#PG(DJr+7Q=rg`}L}S z`n$BEu`&i;bDcI5yHbWqT_KOvGP}@i)ile_PEXh-gt@y(F1YcsBaP#MFVYU3J-3HD z5a!f=d2{U{93t_Wtfoh8m@nh%&7n{MuUj=*J9-DvxMSPCuG(%i;_ztCt82p3Z@vUS z^^L+yP0b-$_lxoT4+AGnZfyvA)t0}vco0RTTf~^jxl~a)pjx~-7dGUbJsU{!^y#g; zU$n1}zf(sBWbKp?%@kkyS_XldnC~oYktC$N)yuyzS>B~{u`{; zcLLI&_>Gd)tRvY6|Fm2qpxzB& zq^-Qhdjt#E7Q>xLzp%D(x<{oq6D&%q+qWOC28+j4^)AN10*cf|0`8$*;6P@gU zn{`)T^N!YIr~{wc+g)@>aE`VZZblHf{&FegR}(hxh#JY0&A@@As}2>S9r)_KR^;DH zKH$mC7AR~`52236W(`a&DEZ=C+ni7cvfsPccyP2CSLs|LKg?*p5=fl=Al8pIZYbGjy4U&IYy&Y?xbry&F>KP9^luMLJaRp2i{Vd>S*E{1?lTUg4zc7oF5ENz94|K}b^Jtm~5ZowwP7!UPJwO=B-co8815W9 zR$Z7VZ*MiiP2R^>)b1z>(jfikz_Y2fUBK%7OH)437k(UIR<~fU0DYeP@Im6A4w+y- zUHHBiz6)zQo;kvRg`nyK*8_Xu1*7~tMUmS}^qo+RUwS^P} zXP)^}ryIfL)`;(?+I(E&HL6No?7|HeMT68ktuRE|nCk4$e7o?YYx)e`54v^in z*opf&Cc1>5)Wec0=a=-6ZzR{z@j!&|%X_E~p5@(Y1N)}uVvWQHJpAzfqpQy;z|cGS ztK6s$_1+BqiyO{I1;M$N^?yRa@u9Oyt4|7~C0afE(xw3yt2VNqNFtu&hOQr$(Zr9P zy<7DDHR98uE6LWJ?S_<4Rol~R(x4rl4YwtiBj>SAd0elu&_qeH`S8bf>}h|K&a}M+ zcP>P>FeL_p8n>U!6|P?3bX1oYHYM}l9T{T|U;3a#7S+8qN+IF~-W^ zGfwZEebaF8pj&fA_bDyPxveFlpCr9!@gMFmI zPpbvZgz8S0hJ=D|ZNrrdQcbu{pY_-x=>rDWGB+5!ZUyGMJ&PNA+TqFI4mt5RqcCO@ zu*4=$=D~&r=VxB`!j3#`uMf2)pvh?K_*S2aot&1>E)woDohp<3^3emB_H{`2J{O1C zhoVPDjOlpbLhYANH8n7$d1o)&?Equyo*26?@nF8*PRr?c9~km|cKtXVhR%JWj7_Vn zfOUA4MeEckQmEl6)b)M%uQw&J{~6)T{|j0vy>bh^<(;Y1q&NPhEr_f8OeKnY=*}yW zy+!(U$9tRk(jc@x>aEi+2Bry`-3=yrXx{hT>pz_u#UDotTKb<>W6{PK)qj>{xQ3_i zKXu7s+_2%*Sm`n1w;Wquy!e{TAy?jb%$HMPrn@B|Zm9&XjKnm56w5`YmV0YV$vrG$ zmLc*>@jdcYY}ctY>c_RG0;ilZ87Tc&zD49D1+VwqtV{L{!>6Y9J(gRFQ9k@k*dn?2 zm&t774^wBgQ4sX4cA+JsLg)9Cw3x0v1dg@f)=4=|y+>(mwTu> zSH~~7wc*w`7j`>FXTZ(z)B$wk=Vx=FLx--4S%MX<-rFa@(DL!KRw&zu- z7O=@czor6h2ahBM{8t1rk0R`J&(>hX!tqz$LH#)N{js&QTqQ2d3zhbYmVrV_Y*JlC zF^COZcJL(q*Dt@0Ov!z%1EC(4{enkEU{BT?cNW5X`P33182Xp=(We^RUy{A@%`wQS zt@VMQJ^BtJ63t-E@EO_KKsXwjQl$aIthl z$1R^ylO-yMTcbD>Dad$9t&H~ri5ozf4odh+^tZzVN;X|;i~v?W_8?OL-9KS;AHm1sUQ~n0B3WxP4L(Dk^5l_`mJIal-7g!JN^%2V`E5iF zB2YKCR0J>7Ba>D3mC^c#Ikk8#$Z{NLrHJElr8H*um?R=))%vrMMt zw^LDIY~2#SZ4;ObHohw6?8eIjM{sVq9jd2XHtgx8!gzb~>+q^R*jbXvw!wmU){TdM z<`E8|a)^Mv_i{J9cYEOXCC&mk-KSP-=J(?GI8S4Xasg(2^jVQ&&qI%kh5PMQwDUk5w`QxD5y(IS`J(8PKj2GTY{3$AF zg`e*7=8OyZV4!yWi$in*7#x|tQyNYIi@X%t#-HRJfA_7mo687Bh&p^aHe7^GUpL*c zY9>76sNqfWJ@ptEc{ldepFw15v1<59_BuSLS`KoJG5W5Z>p#gX;V;F+m2_Ew}E`0pq#FeS#~)mt4uoXDJWtJxQ&6C%wh%ZLfr{p5um zUU!PQqB?P3hRn|P#xJ<<$@Q&k&g8&GZ&uC@t!fmsLC+W*fUS?VS(>gHgr8Fy4sJ=$ z!PYPSWyr%c5a3GToLPl%k!8aJ9^%cid(om9mePgQ`Iq0fo05GPf5uVa8v|%n>BdJZnjCO zwzCHIa2=NBB^;Qot30>g3ZX$5|BnF?{pzHe_uQ)}y1X;Nw4&TM-uwQDgeNN$5IV(!HdjHEg$P85uR$oUuBgvs}Po&Sz$<%LRUQ}!68oF;P+`3sZMgA8=$ zpsJX8d`Gr*Vy!CH<*+U@VSeZP0?6;}^0E372=^p*?hHFn4gR^^tly@h;Al2`T?lI` zmI%L@k|JFAYh(IdCm#{->USTn5!a_!p`JF~N=<{wjP_Nro(xQ2KE98W^dQ}6PuH{W z>B4)NDuOS_oMv_ZAWxHZ4a%j}TDWg+2i3t%`qSk7V-uI)w8^&)Y|w`3n?D7L%j}Oz z4a9(Hok|CfCh@WxQZxmln$US&?x!>4|9btQhl>w8aJka-bp9CmyX0udc3)4p?DxI2K8QDhp=ME^&J+Vy z;=TTaxby@4UKcDv!?U|=Qf>X=qM)_Lf=dge`kzSj zXc)o$T2+!aYG_zAAYpiEJRK|idY@%o?ni!+qzt-p4w^~wuAvfcQ2l$GaQ(z@;99x= z;Y@jsOl9@5#?DHZ4-#QB^CaiU-4S6c_iXUnXp!)7rUM7rhszGV zXn+S6ijP$dlL+^I<5BSg4XAka_%i=WH3+k>cO25`f#vbruVxcVL5?}-+Nz`+yt^uP zlEPO6M<#cdKN85sMkp5I8Ps{vfjJ#rm_m(YmaQ$!Bh@U(st`;Y$$*x z|Hu5tL$ZLYHV(b>=~xl3&Fp1ML9w}Od$Z#z!Q@^UJ+O?tt5YtN^!gEAALl)7x$QJ` z6BY{)v6t9FnSVcVZ+OnTR>N0^v7T5;uiz4ZEh$xyMa z_}nq^R&+l<-h9QT2w3Gx9^dTff>Jm+*X5iH-ooj;FS}@HqvxwVE!c&lo7%T2#*RTv z!TX_@$CF^JrdJlDT?c0-R9afLWFez!~TG4OI@J~s_zP>&>2yy1P3Z(SG zz{0RnoN@;^tg)42Lu z6L3rHexUAB1=ofw>lZFHKp!3p1}UcF2UH9{CiVhd-^`)g4?|fO#IJAK(}Nn&9ac( z-=Eif>&mW!BNrOq3T>$arSsoUPP$T|BCz%GgZvDX8`Hg1I~oDJWlqspSqPzb-_PwK z@AQrLmnDK1hhXo=JAy&PyW4+dkH?$U8TfJMH1T8h;xFNgB8%o^?>pe+_;5!aEWN?gApZx8IE1}El4ETE> zYv|T&?Bh0kxBcQerGg?@*ts@UU5K3D@-K>4I=rE4B>v>cngZZ{aWwo+dkISXRj@VN z(up218ZH?RhmfCTLNt8161Pb?Miow2`G3%V^(cak@5$>tUu04d8Z!zui9%;i>XGitRKQVAqL9v^OaWhEKvKJCnTG2Fr z?ftZs9;A3!G^))K9y`m~!xjBazz|C~e%PTFKaH2}<1z_>@$)9_P~zRsHb1;n9kvsiC<$ z!&|Y1$&iWdZa8{;?Q~?b>wpuL4-9ULB51N4qkR^u25ug=0ONi6SSK3FbFa!9ynT~| zQ->nAw17>yD|jtM1kbi3YoNXr666S zxAc5}6?7dDw|GNx1uK?z)gL;^KJpJ!lZJQ-{=PVPX5+d(ypXzn@r+P3Ud}kZ+B>iU zj_xd6Hd5{b_E2R}eMTYp>AhR=k|g%gM05q z2FiITH_F^3xnrSs0z>X(53}F%?vDHY;5B(e{OVjPhzb98IrU!&-qepbYa{bw79EP$ zgJZ-KmvFm7OF0tx3ttZfE0P?L@t^gD3pAK+{oFh9rv`X;udi5cdIvleMqt}ZA@`0L znp1W-bUc>w`S_WNoL$OnyCh36(q?z$mKX|rNRaQiVBCkVevWn=F|34qRqLKtnk3(- z+kfWo`V?%NIKQ*xRx+&pd{*%O>1xcM?@_)qMMvLedWg9d@$4-4|2Wpvh5cc9GxuK9 zp^4&c{kMB-P-mAMpIPc4taQu9RCkR)Nb>nVt*v!f=DyQ!%()Ubnyh=uet`m_`A$-w zCc2>isYt#0yJE20bh3s!j(9QN?vryl6N9^2*4XTQ+5_z(t^+;)n(@MDhNrtyJEX6Z zt$w1{28~Z>2ZhIrh|gX4>1XLy*nR%R)(~P>`Kouxo4|1$uq5meNg{ zKs)h^@mW&};Xp=8Tfcpc)=O!t9{tXS@Ae#l9-~cgyZqy?M-QrT>-mf=4SQNZ>_%(L zE58=-{U1f={YdruhVe@1Q_9Flic(riMgyr^r6dg_DoMkL79pb|$`-OmR`%X|-S(d6 zIF7w%q$Gu+@ALf!{O~^Sx97R9`?_9N*4K|>YwPi~(4A9CVk1~rdnQ-Jj`SS~x~b`1 z$VDbSN4K%!n;^~-vSp)D9R_l}e8X6kkK2M}Th|b-&Erc)1$qiPaN^D+k@#xi;;xTn zddGXPVcheh1<8fatv;G<81IF9Drp}#g;9Yiep83W4{|?TFyq!$ItMSrHxDvv&`@=A ze6FT^BYwI?H!q8%p?2w0tAM9xNEN^;&GV91Ndwrla_;{)&2sHn?&{Ya_nQ#an4z+h?pgaPp=uNB!$u zNNmj%%`Bp#+%!XKeo+^WF){33A6X3%Pip7J$(*FWGb@d4XCT&0{ng%oFCRDFkH56) z*#_r~micB)DX{jp;=ZoU!)Q?RETn8(4?0}*|NA7Fdy=6_ZnPm{drFDXMo#RPP|*$=%E`q7|M z)Qag*J&Ky2ViRN@fdJOmG_jc$aA}~6>Kr5e8cjm6Hm3P-?7omv>c#@J6)uT8{~rxk z_kVc$IE(12=1q^=G^&XXdz!lYV+HDO-j^}`m=5k7^Nva~Cg`kpZMA`~5b9MpU+o*p zfu8TV%_$0WP#%;mUrv9Hg$uTf>KjLJ{o)x*f#p8D`fcm?NWvlDllq$dFqQ)UMr~7g z$(;MdD#;>w7NJ1zqf^tHE_mws$B+_3h3+yIZgebwNMXm{Eh&gct9N#96z;;U9Q-zg zhrE&ZfNSfJX(!Bm2=UI^*aM@sn?mK7+ac4$%KPP}3Sio68BlY(5e4Rlt2zs-ks(dy z_^L-Gsw{D-R7!OtyXOO`V^i6%Q|}cA(=f@4CF*C*eEEnSDgG~x2UKCp=ATzu{Cgql zkI_)y=3$7L6{l&E{LFo^|CBwn=#ZcI#B76A8%&R7s4RKa14c7%y5-)B=d+&Nua%+U zi}xO#Ma`|KCz1E4=wUJnx4$&F&=!qK?p2qKordAZ#Uu9)e<%C1F|o*&~kpyE1LzrC)C#P^jeH+qfihl+eBdlLT)L6)I%)Sj*7KpJt6?IFMKww+bg zJ7oHRH>4+S8{spw*=i#HQ7Rg5(g~*3Qc$KUXLih$0{laEvG&d+4=w|<3~&1&X@?3& z>Tm|EGyW5^DdRJSbUJLdf7OKtA3U>da~Q?ahj*I_yZVv+=$T7n%tL4=<+El?)Cac& zzPq@7V-?Qa`QfY@Hx7S0)!QGb7GmTvEtwa_br7KP^hmQ*Fmy2kiFx#^AYC-TL`xywx(mW1~`r-OllF|jv{xU&1`flNUja_*C-jn6hRNs z>fsUGob~L8{Euv8em2#1L!lH|&K7uHsT#rjdk^Zr$WuUe$iM|7Ya#P+$C|wfRA{|) zcVfNF1z52?Q@oL*A4YpSa^&x}LWFj@e*$j<{2t)z64V?5A&u*`CqqcSbCa!MMEVdW zrT;42^{@iJ_^T*(PEbK&!06~&`Eaa@-TvNRkPc(FcCB;M3r42E+-m!nNaU~F$g8PG zfi$x%YP|1qpp9)XT!!pxru4e*Y;o#Bhaeale^7x#H(Dn`9)D zM8mo-2UE9x>O=Rpl(+BPN8nyXeW%20!(Fi)_m)$89$ce-ynif;AHd z1I?UT@t$81*MB^N7|IZKeKwPV%qp6%cQOxR4fn5g7pmfcGh!WE=OZfG+&FT5yFxiG z?AvFkWMm0@oBsW5)oexoHO|TfG1c%#i=H?TSc5xNcYnE3+6u!LVP^Q5EAS3YhhIx; zL$`;fD^Fwy&%5K5Fq=*-sOfi`ZP!YKCFvlp!TCyjTOp{SMEb?7-WP4WePake-_!47 zHKk+CHyahd@)i`W-gAO=t_Clzor?`qu7)>fTJ%S@_2I!HKa-q=JnUF+Vd}+6gKz4l z2W6IvK;45QK65A0OM-qs|HG3GarO)CXFG{MX>@SKky?-Hua-=fof{!vd?0;}^p|4y zSC?s?4y5a`zAy_VIm{QOyEcx-Vn6E}np;p4s%dp-(q|DFe`~qsxDl>>R~K)mPA@!+ zIJ1L`JYO}12%l4#Z8&;W5}yyaOoi7u0PK^@$!g$J^Z%_>H@^)*nP9<>Jg7YJQKKHKUHE7f0!D`ijH_g zEdOrZNy0gKCit^(iE#*=hWFm_A-eBA?hk(IgfH~ohOKt3T_qO2R0tG}>4Sx_PflxN zMzGQHhG6XJM#!m_Vv>!gf!<1h2SZ*4p1EDlm)(;HFCNWpZgtnkyKX0s0+sX^$S1D- zt=0%zB@2s%X=SiYQrNfGCmP=8oxD(AR*B4{nr|=povCrhV^YNlm!yT2)R@#_ub%ek z^toZIW?DS0?A?VK&L32yDY>Y8e_w0JP$y*Cyn0Asu7&l|(LZv`JAm!59&1N-7qGK` zd^wWX1=TKVME)o>An3fU+~SZ;_BE`PLz1QNO>3Igahd{sR|4}jms=s#h30s84Gjlw zg=j*jVBw@tC+VwA{&$NZ5`j;5X@2TV+4{ zh2*6M{yw-NOLQHl)GKZ>Rxu{|-$K=u#;{Rj-sKhOO6E8dgUe#CEJ#k{ z;-@(kl8ZTT`sL}X2WwC$@V_U~)iubx>iT3NVglFI`p0^AP2p4CKccjZeoT5ESQ?em z06U*oj(G%T!h$JNCzD4S9u>TF+MM`ZJX{jJp?w%KUH3a5y3&Q?4`!r4KS#_wdSs)g zNjDzaQQX|iQjLcXUh=2#G($>pL(#FfQCJ{#d%kr)9ecNJi!ZjLL)`aQnL{@QAYi}A zFy|r#-h4Xib2WSt1^jc>B&!?2^_DnK^GrR?G5%dHl_*Aqp2O7C!W!Hg8t3|Cr#kZG z#H}3QqhXdtgV;z*D+vGma%r%m0;pnds#|oMpxWsA3)}gA+)!=Xa%8?4_iwzvPa&rd zyd*xlG0?-IQqW}3<3k1>ax=Qd7ejRF_4n>hUO+gx+s=hXxO1kl%aU%l;;~t+x|7|U zhUexD$C}7IudwY=g@7*wnNG8*bMCIjN4LZ+Sw#BaRMTy}19^im$eMWb(E3r}%RgbB zWI%(z9k~?oo@8v?|8cL==>gbfsX&F}?I@As8uYuj4ka4v*ZKCe!F5fo8(ZE{0Umzw ztXrwWUw;nUvXH%%g5#|N-ZyzB4xgZ-=djhZBv{??A4xjVH0 zA6&L+w;`NW`tT#4Qo^ms4SFZl=}kB~HvZnikK18TK;!Kd=UR-~5y-Ay-j9C^lvfxX z{83)NByBt4&XrGm4)m_=z|xDY-}|0tg4EG{OJ>#0pyz$+-npyfJ=*3__Tfe^>^|Q8 z%}!_-ifXnz&66L&ug{ruze$YZc#nH4&)+)q@)e8kxY`Iy0ms=b3I`!W%f2AgE(bq^ zDseyhU5c+a-;9vV9|DgJu{x(r$T`xu)g@LY3(}5qx`$&M5?h#1SC4G|Y@l0ig0;JF2Urq7H zt0Q3YH>zpvLMJvB|6=$_^Z;{>O`p~X_an7PCgOby9r#bpa(`%|VRcE{wHNw^Ah7QK zr9ZK4@O^1s;hbR?)P~$0bdqdGC)s5&6>cNghdv$4?Hy+G|WGn0AcQ@M$&;51uy_3(+F|j2te{x*rF_=;z@RVI(i) zz_yF$w&Ky07gj%*MlgE%8|S;jL&(!JDq2Ex z>Z7U6D)xmvxEkNRdvC)K4zxB}1+1-rycEUquw~MFdH3_jU$bo(W%ZM5fpBlX@=Lso zy;+6-zW6`pwNHR9t5C(oEh9vS4CC^?GJt_vA17XiWMQMDtv;*75bl^sRh$r{0Z+}9 z+fjaW^l{_)q40zPs;{pV{ZbwRvAvRawl~zl^~crW?B8kVxoV!XQ6(30M6VfUxC+VPySF{5cFn_1@(( z1O-REUi%exphAURK$urE?D2F+oVeWxC;eX({SxYiu{RI-+vLi@@xySxCjxM&MwxU<3L-(>_KfbWo%d_?3Fv?zLGcdEsN1e2) z;w9@!bX`1g$p0f9JRhGJ>yZh-?|$K&UX?Y#XyY<}HRKCgRDwHuWjC@kI4Zs-?~jtJ ztm4H5;*SrNzCa^ZZx!nkEX&ATa@9;&xCGL~E2Kq;u3UBa^0Vuo>LEMp*bkO{q<2Ka6#ME?IwXcMv%MOF z`0vBM+%7nB@-q0Yl!NvC*&k6tWuOo=tUlh{1f?RfC(j?~Md3(}>u&`qaQD0Hf=T%Z z*gDwVTl+pA6)pKyw;A;S$D)TuD6JJo5^3A#N2xe{EyuyHFc^b0{P!w7&%iCvT=rC$ z^x(wnZ8%lXOZ+3dq+eKCUslp`u_krWJNW|*Cpv=53XV?WgWG+vJNT=>=|)_2`{N1-tErHF{Av)- zS!e8L-bVUg?rxdOtQ>}Y9u0R+tCb?r3gQkDe;~UXb@&>88tmcznsSMM3a=@g`=&EA ziVl_!?d8jf&iCCzUW>UAQiJ!?@1N?%wX#~xr&h^5%98UXw`()Zc^GGZdfNd{Qsz%P zg$=-7#q}&c2L0gb!o%RcVF;bqs2|jo9KrsC?Zw;n)ZwYh^18b#U0}6a-bDXe4xVke z%^LkT2b3Q+aCoJ3VM^1n7>`DTy9x9bYg@vZ-m>MrZfhE*)qBN{mDNIV(5hf?Rz0M# zHvADk-wLDro?M>)(r{vnapgnle7Ib(kE@}20C|~Mx-V&UfRnb!lIxy&h<-llvj5j8 z7;d-EV00USphI(S_Frg((_enf&vjN}w%@nN-9}MB7qFHzY3%`ND(Aja+-3N0ctKx) zPQ#(ivI3oCUvlz0i~IQRF)&wpBos#(1C=cI#g}KtiEd$$P-5H+?vso28ag>Bd1W=t zgS?NZL9Y`X?1a^Uq<-Qn#kAkWij+%(#3#OTg7ZG2a5W9qrF-Uql8&+=bJh zINxWGo~buYZTEXg{z5meiK_)(7_Fsr%4Xt*gSEeAueL*-xyCKk;!f~fV$cd5tHaqp z+M&|bAJAJruWpgdTW(bLpzpSwC^#B^SVN8mLkkiYl~Rem#vS-NrJn}vt^OPRokzh! z^T0(ANCAgk=H9O~EkSz6oY}{kDCl|A-UkVMcQCg^}(3$TzCBW-+uKk3B2SW_EB2K<$9h>3~S?== z@@bGK$Co=;Rth)iI=fw3$vmzRz2kh>2&Q`FX8v+*gaeWXtS9w||HJmd(!&}Ggc=`x zNd4Y{y93iR&YY&fuixwJYLaWP;z#s3!J29iyXIeR_`VQk)*Wfy^@i|N87*JkoE$@g z&lj>ajCzm~?qGS1y#=*cAMSi)*npETPlT;U zkbUHMQds9_IQO02v7l28M|^lq^T>VsWkygmhcoHvVZ!k1swNqr*?_Mfr-VA^hHBTeieK0DR}&lel;(@ZYUp;lIP{LG8We z+2Y-;7`wYagt?;+6TH<7pYM2vt2ax>ZLkTi39%SokZc1FhuyUu#|huI`;#D(uDYU49G#Ckt_>^WmTWou|Qe3y$lDnJQ7BpK+nSz6rC% zYt-0{>#?hD>bKIzYW%yz^-W=4H+=B$zU=5qhdl?U+gbt#U~RD;?_KRy)b}%Roz_aj z^BbDQ%B6~+_`OR}-v^RsJJk68vs*t-%~|IR2NBNZzrTCAED?uTo<5;U(7@&1QMcjU zO}I<-LT<=oa<6zXVWN71^laX1i@&h89V$QMJZ#)aL*9L&_3{tXK~nZ}cxG2Aa&T=Z zR(YBVt@S&!$0X~39^VUXyjdnEZ&jeMG5CKKOvfeB zb&>jZ;i%1Q#^<-Q2DVl;>(y;9MAh1j>jh>jUKH5Jy^KOXZ>dqdk zr~|vf-cz7M2N|K=X8Q-eVAX6&o`az!Y@N~gSzS>L`Smxf_V&_|9%u1qFVR60I>l#n zCbLMNiXM$`+y=(QzUdrNO@UkG4OjK~Nv?>kH#eR1+U}5i{LyXpEwDXXU*;4&O8!Y{~t2 ztAjRGmkvfI?$+wp^I?zphfNhLgl~D!BsJ*ZAQmZgo|d+zfWpGBxF~H3wi!2u9}yXX zl5-rJl#)B)T~XzG?|?4IZhSGiH)9x64E)2>hiIVF_EdpCaH{&!1t zy*tX?9%0<6P53e<^&;JbcU$?Grf7bb>~|`jgp<+SDo-3b6FwTFud^CV?>1Ngq zwL1{Mio)}h>tv5^VFr9(T02qdrE0P7jmH>!@^bLu#CB*E>tLJa8Ue8@&x4M1*TP@* z&zI!3lDxSy)qbih3vZ%y8&~)M%1xbbFUV;}_7|I!95On|9PmTEX0#fbOD9hFR+U5k z+C|^vdWbWwy2cK(&B)-G^^0+5Hzx8*Ducm3EDztdvWP@2zM&=oa*E3o{*NOR{Q3 z?fUS;c70iuA}i=E{UNgRL<7nB?cAIm)ryN7`m(RC>wp#7X5W+XmGH#o;0k|C8%)Lg z=b#=!d<^p+5}$tk3gYs!1DwLupkAe|6ZE(m-|EQkp^S|oXL4_#{jLf)W^8!p!34?M zJbBSKDqe@&g?pUoW8Ki?5}8ByCjPHr9$R0NHc&4w;QBA=J=A4gIPD}$cxz|dc#rmF zg2&X?7K^Sdt2o6EwL zEgMfa8Tfw}oPw)oB00B{d&&A!Z6~U{h@arhi_qs}KjP!D>7@Ni0^a2<>V8M^_4*%a zvabnO;+m`Zjr5fBz@cuV($&`uvC{jb*QOGGX}a%>F7F`lri-tWxkp9a+5Hb?h3i3I zGIf8u9Svo-yR&NWHp3Gg8)F~B0Sb;gM>YBAT#ei*3nfi%agcC&nxFLjb0E(Q-w9AwofZ4lH zUbY|>rzfUzgG6X(ro1!Qb!HNUChjS-yGNmzbY!4XV*}jy{l@wP;TIN#f4cDODjj1f z$t8yCUxTTa=jD3`E6^k}`|Rwy3b-L;B`obndU$Q?E!@q!VAXhww0jE`Y}#oOY%)m@ z|3J2Ik@%((gFk*vpGbzL;E-lMnPK25XyCa`{17v`UfgPgH*u`?;jVo*iEmE1a+f|! z8?s)o-r^SVr6kz2*8eQgH$}I4%p}eCVaU&2TK^`J@Xrqk_X9g9XmeFXGSp%SnrBT| z*Ss&me!F{`)#C;5^xU_F=K4X%-s{%&m8lkESuWHPq7XhAzLR~=up7&gYg5l%Zp8U% zohyo_-VpLSiQ#O17FJp{k8G7A{^hS#NX*@G`n2)2xNv_b*niNV>`VMxz;<+(?uAGn;=Eg7#{!v@r^ z;2=HbH?3_h?)cUWVb5W4!p<-0jc<6$;bH;1dtc9tUhtHPR1uTsG( zwoqYp*8tpN^m}QqHwdAsJr=axLr`m*C_cr}M|ws2f8~)c7`Rk*2d)r^n6NZGR(DH8Vgk(3gi?7D+By&>JnTRnTN5aiW)X4wX zmWyupwv~S98G#tdvf|v~KK!xEEVSva8cfS}rM&JQ#g%H!@Z_`#e7j`yp6O5x95i^g z|Kz1!G=H_jYv)2a{**|xRk=v^BHm66ce;D=Uzx0&hgd7JWXWsrE|r0)-nyJl)oxt3 z@x~}aQ6s*q3s&*F-GwXnMWoFqUcl|ZHz=`4#e&+q3mXp*e^bRwtj9^>+sbRZ;O{sL z0gQ6BiN&R8TUeoYmN^5YnXSUAZuEfIW0p3{x)CU}@Rlj%F2OK4gS&yRD`Bg~Vg|c- zKQbz}NuTB%MAcDA)7I0(?b-`NFM;0xoR>AIdA^x1mo_Vvb6lutI% zPz|ADRh;>wASVh2E=`18E=SmQz}lqjH63hEl`8%wbL~&nC|l_(9k^7iXTRv#0iiCD z7W~%TkWnY|yXp+-(U}sA?)pLQA#b>Ou9Lh1uhrMHclg?|m!7XSOZ*%ME*`8YxiKo>8oPf^57PE zKbHKTipCKhw#Cj3i)&1W;hrzw?4PlAkYc^l6(G@UE%J^E@4bz~l5)0C(C)xyWhMcEZ{{+|wO`&bl8^sHe+ z>*0hx*wSujWXb`Ez?*1Lh=gidyATD!*N4z#Z=;feqc*ZrF7FvLGxm4 zhRWIzNRO1O=q!lDsZhPSl4BKkPEhoczEVB5DoD5pds4{#=$~evQ3(_X+x(nODujcg zckfzmC40IHDiaaEbKp?>CBwDc@yOX8E96J^88!{ZzNgIFK`&@F{FYP))F^G37JJ@; zpEACwb`P{dyIaIe_mC^9;hW2^pq7LBMd8ecNG8H)kn3|va z)IdO*sne0kTwEXDEvi_ajQak;mnQ9oQCpo?WG9(}7m~!T5&;p`NNv41#65;0hH<=m z&Wz%b9Tx`|f3||P3X8bU-6S+A^zmQgO^2I~8*7_r-5@0W>Q3Th0kCuLjb`rY$7eg* zeAnHl!~W~KN{#N9Nv3lUq*!P*}D9@21n>q#p1&R%7f38=u1 ztuJMq%7=izPUMpO`BY3djoxaI=mcvF-cj|K%3ws}-=2+e)wplj?)2FkgAi0CwiQa+ zkYU`K|Lxf-pjxqhFXXHSr@mr$H@j|NaWz*G$cuyFYyO|aj`iUFQ1-}%&?e{%v2YoD zM>si+BZ^J$Iw55=NM}=C56Fhn{YTQ9A#IYL+{{r2VVRvG3UOH|wP*T)pONi#&8t>2cn+<_u9cS3xd+u*B?0sq69OgJK9a7U~+4Srd3JX-?&l{}_>Y*;eqw8^OkuFAxF`Hrg%TY}B2sxj33Q;U|Kite5 z!H9P)*(VgTfql~_rwkSHJ^YBB`BzMIH9sjezQsWl&rV2OtgOdJ8AryfT3g8TeIeM| zn+XG^JKxFYmBU@yy1ONzSx|QQ)bhE*gQ)Xz&-S-ue^jpj>iV=!2SyH0ofFEB!Ru{% zw>C3$W8m)B52SXNq5Xl(1J}rXTDwC=DDq+kG;Z4}*ua*Er6yanta{15dtQC2eqpGF0J z_7I=1);$M~NrfuZ)s%dmrV)T#mXS{ch~MS(5z14=f0elR#u)Df($jDuy5_=^zXkMu zNnhAW_{0|h3TFiT{4cQ-9iT6FFfI!0`k#$@8 z&}Pf`w{e4gKv^UD^=vuGi7#(CVNK@Wk{ea_tC>8?188NZp8 z?0d#>Z`G!Wh8H!^cYaw|$gCA_EnIOmh)Rd7{E)1zj&=C(ta?}QeZoteSii9;E)Q;d znF!un*MbMB$)~T&lir!%ANOppNJaV=NAuYKn&9H5!IVAKeHg0V+LV8}5B&B$(X^~} zgT*x_QUhZYaC(vUTHA2|%`%3U)kJ;Z4gW5wXFPfEcEC5X{(UWeO)--B#XUm!;U&j} zT}Sa!!{!8+3^IpFb4&(SmBMbfjWLhfozJQ_AWxW3z1VpQ?rS+j z&D0&iD8(%4$g@QEea04}SyhIstKz{^QhkKO_0Ip9N&pVNQYc7a9e~+XJ z{PLcA36vcgk1IIgjT*gQs2j`+P@`pY;$nY2-p>?hNZ;Lqv3D=76QMKBdg z>W>NYz3c`a(T9d*53=#3S8wzB&*^~QCV3sN<-va6LDry28jkGQ#o^ghk9W>};{C9H z9KB|>#Wt_Ag=EQv&Cxe1wT$xp4K#Smw7ti_qyRpdalBUkHiTuFO~+0*q{Aft+x17hDIi&AbnPg4PVT}| zqK;!7SebO%{%~R|7(QvS`mjXD4)>EwEK3xuaJ*5&DH8Yd9ZiFosygEGmT8dhF%(5S1%yI(vW^OE^T zH11F7elF6>m!->-t3C>^Gd0?ytcQWGAmCONtqZ@rQeqJ)COv1h`zoslM`Tu`d2*b) z7M6UQZGAQSaO2d+e(8tJaO3Z^+)nv6O!G=>{!>rEZQks*caDsLqEe~$SF(rT`S@sT z-JN0B-_rQ@iDD&QJz;-epX{eST%&7q=i*^)?2bN}y(46=$P;+HsD8RuePQBYxIaLyd`w$`V_b1Ef5G z-)TwZp$m=+Gd*XJN`r!^m$#qRj6uhrVv&fp zT)6YgQXye0$#Yzs%bDY*;icI!EujlF@G>WF z6eS{3h=%KWPV0SWh4&|pnx$+h2mh>f)X|P^oRa2D|25bOZn}Colj&8Um+a0ts67la zUppnZLk2+TXi~2JzEs#@q}OI^6l@iRW59>``c%*rVV-0d2gGIdcczK%)=A<73gCzc8={U$sLWd zv!D201{d1`j&Cm;1`30-*=z1T&<@wTeCSvkRxJNyQd>*K2)mf}-D({m-D%hH`gAoK z7!4m*oa)B&zNw)@j|Z_`@|JR`$rl^~zm93*w+lGFvt7pEt^_0dZN3E9pjxnC`i=0d(ydBf|DD1uS=Ndv zl0-jAZM>kdAqUw@B=}12`NQ5Pm65utO(1?sJiPA{4Ub=q>`4{vfO)ss&4+oXAmpvm zbrn$$Job2Xr|Qp643vK`f1{=qiyt{A9jYfjbd|2n*R~OkH~aIJkeDW{^I>^^kNm&M zd3q+kZ;A%P`L+v3wa4N3z2i1Fm*-&R=+%z*leM^~Ty@LnP$ulr5O+@{=PSv*+~kTF z#n^GR^Eof$QIGRs2*blbQ2Kp^Z}b+qmprM>yV@FqTO8LGoNgQg0exq2^Mi?ag2Ap+ z`9=-eJ(YVEVv+}UJYym&6iHrlEbB~t_ZWT&m67%RQwcru>m&@0*I`3n_3PzFRdAYv zx~as0^w~cWc)V*U4_7#U$7o${fb)x2m*R<@DKT_fesEtaL`rW>zT8p;%;JpxD-Zjz zruSKdLrDm(Z<3EKKS9MW6+dGlsriuYoukgay$gK5a}J&7Bi!sNd*;0#h`&V0`hxDx zA-uAqvRc=+3r?Os;>`Lr4EOAN9{l%kAu@5c*lt*D$K0sx)+Xw7;QeT?ES*8PLsm@H zW2B!)q|b2cTvjFg4ff62^!gJBKl@&MmVX#ee*N>7vF_O&5r_b&krdJRMfZ&^l;VYuP#Zs!s}_Uivu8N`1Re~$+L&ubsL z@$gMYqvrfUSg&-u?B2F^cw>`vS$KkqZ10{H6&$F?bDmGVZ@jIBN;mg}e5Ej)yw!N~ z|IcYtW#{v6vI)p3{otL+u5{ewD7#vrQ43e(w2zt2`9s9AnTN`cR`~T;|I9ssI@D@? zMgOp`18AX}|2^I@6J zW4Ob@aG#`c3HqK6;1Ib={4fVKx43O2J>hnx$D}rO!$*@BR)snx;JUze=eTwQGK32+ zW>Xr_RLA-S&Uj;Cy10}_t0&0c{PEU9I}Zj84&Bda4uTv7=RY0+1#nvR!Y4JdXO39g zzKh{(FC?pY&N7jETw=bVT`t=c$o)>*eRWGQxJNxReqY`L(Id4vVikEH(4TTOTr(AJ z7P4GD_pk?!HViT|`IKX=@sFnGheBb#xUfd&Q3D>5p<6Q$pY0v@+jOSYDF{#Ybl>)_ z4Oo0Ax0q(h+`XIWdC@WrdA6kJ^3Yp|Py3m6wQm{tnlWF`dEN%QK26^b-8hH^FRHqp zN)fJ`5&iNsm4<8Pce%OCbpbz{&*W~I5xmxS`7m-qySHg*ZwVCc@|uXbG=RoR zd1iBD|Ah4UgK9g5;AC8a*sJ^fc!Jf~!}eDP`1OcBtXr~&BR3W;*$AJZb+2Od(a}-- zbINNrG>G(Oirk9-mmP~ma|S-?R(&XY%c-q%AsUCz35#pRw!r8ja0t~7;nP{?SgX}Y zG-i9_|M6EZu4kTZvmtqh`*FAbOk0d&Q!S&9GOZgQr8(%D>$hQ%)1;_7Jr~Y!B?L~y z9s?a;*&RK-!{DC0c1Xmt8fRn2O1=`myf}yBIrSe8@ogle1QXr3NfJuk>uK=hgA2!5 zuW0mFkPJ7PB0d-nn~@TQ4p6FM^ZE6U^clo(C|@8wVl5o$H`a^|pmTY_^&G8XRCjk2 z`$6{1P3Nq&!xKx00xVN^Y~(vgQ|L5~TEc~q7uYW7;R~C(=5~43l@snP>%??YF^2Qp z%3dkXMIO8L3SkYc@Y7~6?5X=GdW1zCNV^jT>rH<&iK~GRqr6p_B(L${(2R7(;ds&~I>$r}Z$jrCZdc+7hpBPA^och4dxzfK)k-+n zjW-Rb$5p>p!?K_S!w|)EiOZG*YE4BR9j)^ z;DT?IfIZ$cxzEZpI{=%OGlQ;weTcds<0mZB3Wtvk%CU6x!5((&4Tl>?A=C6vaK=Cr-;INMy(KI=3Tu>QI_vyO1~bNN~i?-|6wqSIDea(qEYMM80^ zJsqviKXO<7T!MQhzm4+}zj&&O3%jSD2UaY^?HFbt9I5x7GB2|GL9h5@ssZN+I6jJR zJ#0pNLE(HFj~>xc!u+nD@+B%tT80nFz4pO7>si_oRf%5m*<)t#UN#0=ocZ)iwh}cA zSadykTJd96k5aXHIsBe4@t9il#_0_oxi>hs;Y}6Gv9``cNX}0gn;N8HTu`cY{^x3V zeTm{$r;`P~y&F9YTT<~q6Bg$T7Q!WFcL?||t%PuM#*B9Tq9IF5{{8PuouE}c_OY_P z6I9-F$p3H}#HW0#H48F*s2o(zEq8nZ-!bggvm?G&O6$v;FFut){+pN-cEYJ3hn25mRiJFPq%^vYaPeR3-_57j!E&9Q zyQ1s_YSB-$J8mDs;(rG7Do1;Oe)#nDTBR=7Ri^q&ndk`TC@<<}!>L$wm0Bc7&g-w` zoZ38D{ZMi@O!7@M1?wVvbqaaNeX-7D>tsVFEPkU@5A^?^155e$mhkM(vsVss3N8jatSIc3zZ`%gP>0YRGR8?htLBS{~EBC>-2rZHke^ygl zabkgkf7aL$G&wUlE6f`3cM-46MWRbO&F#A`(OitzPTNoI!g_SS1#(OW+cByk&1vFG zKB&fh7!>BCW4F`y@b^`1&~+@(-m%RO(!I_a>Bl$1^%7Clh_6jJ&fI?e^YS3>ERFr7 z$Xkk^Zuf|3k$uS1*_sxq={n%*y+s+`)d8D|qMc7E7NAMu{voxwHdNofj_t+eX)JiG z=5w9&i(Y?q`c;$ahEEES47iy#;pOdiImDor?d)L#)E3d*Mpw{!RR>*$~O}rOP010NJ*D zT5~MC02G~?Io96E#>@Ve_x1J5;oZg8k9Nnm;isI^?11YCxrb%+e{|)drcI3TXjL!V zZ0?}aQlH_H>B?m1Y93|;uUpeMK!;HYG5LKK1t_qKidToa;nU;(O(~2`XguS5yO#Ve z5@$k}>|4um)JSp@2fYKMFM5CXt*Qiv+Oj)F-J`&sp_k77uM75{8;WQ>5Rck73{Q9H zw1d~G*^v4v!f^_{#n^GYob&^DU;8Xl3XN;~4CAjwgWuYvt^GHL!E?K&KDTHtChQfh zT3)0=!z25u3sEDGQzad5OzxW_McdCD{@4oBY8id`yySe;lzCzMG8T$o1S(5-5}tGH zbmo&vDx5vbGR9S34LP&Q3Q0W$q_@&PtliWXr{Z-^*%H0@!e3F6mzaV}Clt)iozBNg z#z9qQ&kcYY$w58-?%g=H8HKKR&tjV}{RHPp?_;mL^iETz``edZh7maiEZ#TNNG(l0G`s4TN z4NyP!Mfly%XsEoM88Uu^_&gp44PG^?gM;51gM+rz!pcy`+#`!F{IT$#XE*6-%uq<} zzPr*1v#~h|Eq%p=%fi_F&bA16Sfi(^_y*AL6~FuUM#AH)OX|r#NBDxjv!loMwIiEm z%ek6{STz0P_;30!!dH#kgYDVvINTzh{d9L7vPv>|c$v_^@=CzROd1t49O$_NA!BGW zFFm%QydM7icIcr+x8VTYT`uKY34UH+;?;?4#iD2?JGG4@C#GH~CRjE~&c*T_BF82n z`^q}cHPkvxKiVSBA3=J;8h0=qc~5woAF2eii)!FV6~@d%VJ9t7Tjx99A-$x5DP~GChb~3l-^y9nLLE{}#iTGG1X;zBxNotoB z5=w}EAotxdEKjWmA0Ad`DkVKj%&StPaTQd2!|+0jA*u`a$*s}5u3LmH3!l`}i5{kV zJ0VrvvK$&Ve*UV^(+K--wex9;5WZG#$j8}=;N=q(85hErx#IrmrR^v->C25CN@)dZh?;wtdpmlLh;gIHNyxhmMpsKhY0j!`G$3cr=IqcHuts6>?A1t*C^!BPTLG+tZ<^ z@Xw~=>tv5RyXW5(muk>_fAv%uTOsm(O8C3Bp%(w6Tra+tN#>lYHAKA`ha)D1h9fH! z%ru%hFwro8BLn}=t;;99?@tb!^s`rE)%~l__&MmfD4QweAm#%nPZ^9i7gPZ28*^)F zd;@seTXSFg&;&L!!%F=V#LvMz{YSQ@32)pJd-lVQ^c?Y6#xWD0dwT16d;gueP#)=S zU}Nct-<2G%G9D_2(#=1p<7A(|qeGFG&esmd$7q%Xt7JZni&Z;#s+aiqw4bxj^^v`# zTJrSfA~4IQpNn<&M#gU&!-vA@kRf=lhj*bLr~=zlcTD!8tk=$InlAC<(p~QzxR{AF zMa`=WhF$pFIQ;qPIl?_2fNMi=IeJv2dH8Q3b{r0 zENk1%wGDh4@x1K$!DnR7R^PT{l73N45u-8tZJvjGFLB(VzA& z%H5DEA^jT`yPb>FkqbSB%@m!{*4;Rml8=P{Ya_*G8JktO4@-Z zWK%j$-mn~6cV`S&PRO3LApL=Bjs>Nx7b^oos}nT+*93cRhR)brY{kFBH{SAGt%Fb% z=A}oGmT*aGG4W?|HT-qWOWcZuIBL5_&8uk$i&QS_{1B@}EuE&`Ri$yvw5SaVeL74+ z#f2ZLJKLd#|DIdu?K)%-e3_%w@e$*<(bsYawBX=kLY#$6Hg+rtIhpt5zzb9j6dUv= z`;L~PP1}pXBr-SOJh==UZ$)!(Uh0OEU-q+oCVZQ)e68ng(k;-vpHaZiu?3xlM|i&N zY{#qH6ONpTZy~+6uE#e$?L)`AZI764HKFdoj(e|EU6H=(JLdFs7@bVlJ96A^hd()# z1LI^5%9?q)Cx_^@k-6sg9y@j5yr9Pz*QYAb+;PQfVj~UKT+y6RZR2;-G>|c;4cX=G0jM z&3D`%(F>+v=GxkS0i*eN<2l0?34?ypOL5${;6^)^8eM5DP^OYRS=Gdyq$+$D*m7Cg zmptFPXYQ)G4RBj3TZS#C9&UJW*wM}nf!XKdPbD@^fSSynZZ~BbL{(6d5-$xwE32P2 z@B22Sw;K0(o*c&7?F9vYDI@smxR9aWWkU>GN-J8rSPbzBSSUyQXw(fVmpnQK;cRwp zq4R|=@UQsMc~|)%5EgVYJ$JK~)QwTD8y7G-Ki3M*R}q3*!Mq* z&ciR~w++Kd2}O}8lD0x6(oacODWNGM4NW2`4N?d#A??!8QfX=LeYN*q&(q%9h*C!K z-tRx)ljo`L_r9;|JddLqYlfwNYK)D*evfOap+!CTLp7p?`*195auB<9jEV5wR3$XU z1*x!g&*_c^`C|BxWX^fjjd18Atj}kAx4}W1)txFYi5Kj8K!Rr`BvJC-V+rU%SRjV{*iMpqzI>V zq4arUnC>tfTl7?rl_kH2oqoS1XQ|l4a7n9`^lE29pFTC(HikDI|C(f5rl9asTB*~5 zA^7@txVO?7;-wLf9Xx0^0BZH;Uk?4GKxkK`r|;uIEIpy(Ssg<5_PG--F^S!%=zlZA zo^b;2e%w?qxkSYenNJr3ZZ-o~$lUzSO=+O_S}(z!HxK>!96RDz=b=UCQjK-gG`L$$ zY6gcjp!P>0bSrkDEC!#jG( zKGG^(g79dT#$>}D-OB<-JI9}~MY(vBX*=ZZ>xHt^yO*L#e>Y4zb(3;j7tjW@8zi?N zj4ild$MRc}r%dj%N zQPNI}@J!!W$`x}B!IgNC*;~iU;g`eH_@IGO+!A-uaJPsr7!6L3d>{LW3w?7jQ#B+P z`t;>&$4_z({VC*NC6@$koI#9N|CV5IZ1j0I-YPWy?B;G^)DC$DO@<9@m*8Kcido1L zDtBcYl#jYF#Ba|aD%sX*!_e9l;6GO19mw4Qc(H8l3kNUWKjgh&KmO8K2I4G1{ za$K?*z*or=L2i4B@Tq*rJpJo_eB|t@XwK9I*S@s{eO~EBr9g#>-!EElxm2L^!BYy_ zyxMhYLvTCl3B-rb(0rJ|G;azT2s`2l%Ll>#k7^km~-cYe+K_M(nHQ;+gv3uf@R5R>BMJ?X_in zcf1ydE}0mXYm@%8fU%a8O$Vkc*fN}c*Nwl`-Pf0I_d$$w`Hz!-vcZXFam&4Z1Mu(l z`OQ1XIhQi(G0u8x0KZ;hXr0(Sjwhqe3NEi@F+|}ggNv!)v?N|%M?xgjSO>J-CQH;{& zRR0>xdYxA&L3;d|wv%}}f%Tv!x>$Ctvlq5natMC=P>Bv5N&iJ09LI$hUE8nqQo%T9 zQ)Bfa>0#{r`^dp!2!@kd#+56|f$hKqQ}^$Llf68foXEQ%!ad?#tJ-@Zmhedud-leR zLderUEFq#jV3NKl@QHAFe%y9pU_0Im`lt4Li5b{=jjom|i z_V>ZGj$+}p^#+t0UCBN5w-GWg%#I5%wcrK6BGn^;V^BfA`HGcOJKBv78#8|=dzj5H z8pmGyBkz;ISKCN0?}4Y9*mNg3=WmLgU%OHUN6$MM)X~gilOy|GXMsL+tPycvbczG6 zZ%R9yx<~Nq!9G{1fexhi+N^w3EdkPFPnB}?1S8}AqplSXi05tZHp8o-AF#K+`jIoa z*U7zjof%n2!F?5OXRk?*wXs|$ zvae0u-cmlv+yLX3Y3Gj@QPGy!`VoI|7s}OF=WcAAz<#!ZHf}%Sv6HB1pI{+e%gzKY zHpWIY6^~3SU9ZJJqd5%=vS&GXJf+G*q!8TRac~cw6I*2Y40}Q=7_3NJZ&1?+{N>RukJaNaO)S$mP&^;22Ht+zq>>9?{wP-7Q5-_o zSgEb-Q<>0wL7S77^z>hQZ>9_=Oks#$n|KSE+g}~sJY?@cK`$9aw)elP;LL_6KQ;*u zLD5+Ct9YA1*rJvC%BgS|1oaz6T+%Dx!>2ZC)RrN%Og(Yp)ZbPN_Nh}+yi|inGkF_+ zRqu9DIyv!`E zNH7>?3Li zsl0NM1MfKaQ=Gi#Lo!CTaXuKu;`;$7=JfI)I!RZ?_I)$Pj8NPa7CMmQ`8_Rjp&^|8 z!99Jt;RE46ZEpOSKyq|8bPR{yklxz#gWH|o2f?&qPng!xUgSG<$Uf|ICHcFXT`~=- z!lPBCcME1l;m3I+1?$J{Xr!k6dHl^VN=dU@hQA%c_1a69f@6kos(J8Uz+4@!C~g#J zkxoZRCXMpWsan*@l+>5tjK?+JfqJIx4M6Ab?5k(jj;D^g-b_#<{iM4Tmf_l32&L&i zurZek3xXD<-9zozFMOj}VYrF#0glp*_=Td=S^bspyiO3>C*ttHuLY-gI|?qE^y7*@f> zdT|?C|1R9HxtAI%)r&h-c`xJ!HR8m@v%$G(-MDxlPj;BA0?pmz!k*Q(!%%&l<9O#| z$fHy!g;U0$!PxZC_AeD6o<9~GQ`iB;{n9V%URA-hLsn}cM~GKZ=QXBscEGbD`roAy zZGl9mO|!G zEAgj5Z+Z$sA7@=#X&Hy-n>}yFX?23l%9$but!~)Qa+r&Q%wPW#6KDA(k%{+Cc@*%j zyu<0m(0$f!_1IGMP=e-aACv@qFtZQOh65HX&5H6}_?sb-?dIz)Y+O9X7Y!6-&Y>IN zkL^I6Jr86Xx>}$v;uJ@h{TO&`wr&_W*N)7Sj3Z8a$Ka{h=X+9>J#fOc;|E&~6~3=| zeEAYkhsJJ>hC}MZm=VwvDB9ZxF9xDLU)>(Yqg0{2tE3lldd=CHIynZCX1|v%lvP2r z{ky1P(m$q%$`^fN&%)eXk|QyYk^k0K0ec#peC)GIfaA>E`dn%O zW`4V6#=={K4H7FX*X?UzR`r+3fydpb@mQZlbK)(Ee{QogO6!4>h3AXJkMv={Qo`rt zf+o1B#%flaKaQg}B2x_3D7e9*e^I!u8jem(t{V(a5I)kJv54FVTvYbU-2aTsar+s* zyV4awf+au8Ea?Ny7jJqzai$J;(MTeUcj0w+#kc=`>Vfv4?JZyIn=#^y!%magDsZ{} zarpFXJ63XNEuD7iKr88`Z#*ZPAZ&qi*l~{RALj0VV7uM`X--T>oW0AzmyU5~_mh0m zX9#haS};lk-x@Y;8XT40QDG|Jj8*{Y3-I zOWPpjcSOmZ!4}*mze(u?`M#qkoTr|B8o`TEZTlDI+mI`}BGYK55cF=JIX3n711bt6 zwq|wo!^wL|Aw9B{STLI>ZSCEU_IZc9F3EJ@a&Jm8J8d__%CFVj63)kl_^Mnb zbDg`-I0gmt5Je{O>ovQp`o3!%+r!)VlIFfz7$xo)od?Xin?)%wry^nRsoWK41O?oOM?p4|1!t)AJwkorq zP^$)Uwwet)YdT@({=W?KpjMb&s+?S68U~+lmlm`3jRK9`6KjXMb};{!#FBHX5IJil zLOpNS!$wcfaBDI@J@9oa^X$0}5PA1G<|K16e)Deo%t$3ZE5qKn3et~a(Z6WS@um%a zDKh56u1@U#Z~KaXY6hlMUe;FqL51nlV?qa%!%>pW?^mt*I2Nz%WLP;#@<#q&I7$mC zATJnrZJCpJk+0ukd@w>ZU<9+ahvhHE#Q4sLSKYwS4@NYGjw{nyH+#c!>m+{#VaPfZp zBB*-`w#YqtGSE;C%EzOZm?x63`H+G7MvekVQoEQcc?sc-;soy%{tT4;;CiF!WhzP? z;bxQ6Al#ShQEQB|Bv;aSBmOUEI`lkB*%mYL0X$6R>^_!#lHh|fl*mk>@t0$=o+b`L81+1-6jc$_PDBobPeVleT{jRK*S zLU5XR81C_P81IH`Xbhls;FSK;N&VRlFfRM)bB8S%<=piiWi3u%jmMR*3m@x1>DP1a zT#jMj<-EmcQeKWvV%^HL^T~WIz2ugPaXF-1d8zeGHy^{y(spT`8^OnQ^jjS^4?uF# z@`;G8W1z!(%Tm#(3MS9e-Kp2kh8@RixK11)_Z05HV?M7(Ahp)E=^b4yN;XIpetjB7 z{_eMLR`a9a)d#n?v9qNUANffOcE)Y=eW#vP!a<@Ry8 zp9))4#bmshqhUToVktCm6yHf{C$GLpMbql9vwD71JS$IA^J}#d*z)#gu5l60=iDFe z!VMI>GW>V3;nE1`Zg|{bOL|HCHte2~8>o=KzaUb6Ln_epD(+R&9>)XH2j-VW`^mg~ z+9ySxaJHCBc*jYvCRJXq-+==njeVLop>M@gZZE9#0VZ)n4(ytHnLde{;JxPL@7-Um6omW^3 za@Jhi*!nx6M%FB;X>UJl{VKUGeQpZEm5%M+M)>=MT$?L17Kjgedf_=^S{=%17UL0)*zS`rPfg>z73=U$Q@GU9S@j_!U=J7cEe3oB=*{{DT zCwO#1bF1^PHpwf?F7Dg9c83Ztx8C(Q-7y3Y7>d()&k-Kw^iprJPZ22Z%^yhnJc%~y zYx_mDCb1~hO+Ys&4fgRBvS{UXf#`=Rhs(bC;IYk7_OU=e@u_c#N-yxk80w3@O&{xl zePmCEs7M#oeV5uaML2{{{nFLxwoc*YKb)Me&HR9=LGYvE^BI)A#@;S}s{vo!iZ{LD z)&sQlb5}FFi{Re{nG~lzh<-f_MXD@*U}np)1x!bQn%Ap!DPa(GZ_#Oc4)o(!fy~ZD zl8c)?r+1c_GY56q%$HtHwxibhBdS)do%lrMU7@=K@iy#|*WKmS54L}J<6*QF4AwtY zp&1o#oOgJsLwK$~4~remv<`=yupbHv9|=F{;pBseDAHH{kr^0R-vR+2Kk^^sARI|i zx&1^ zAMPtPSltjLhTAI2m+wWxDCb+R2O_m20A zu@N4U;P9{=xz|3tlc$~0`UVrF9vaDtk@=dmceB_;C+e*Xo--AHi9T0)SxV=I!BCT* zQFB`tIJ^B0WFXNy~d~qRGw3cZuveaVJ-q%0SwvOCGHA+H% zJ|cedtGTBhXJ%o{gNEp=_oGnX>&f!Cr51d>8x1zzCZ1v1C>z!D6x2yz6g#pwgwM`x z(J%IzMa}5H3|}qkzzHuhR5=U-VbuDp9-D-L@%DPN{(fj0WigJv;Pg-y!#Yf4$+YbK3;=6HfvW_*(|`rLg`5(+EGZo-4izX zs241y$HwTL;a$!1C(UpVKXVP{SE$rDpF7<6mq7-FfQ4eZt*vf$%&` z3#Sh)Yvmz}=uhYT-csDHEzpVMV=#A}TTxD_9aQxj_WV6a^7>Ii=a$bRR)+F~UA{Ps zj*tJTPwscX#Te<8J0~-+k=Kju{Ha{{N^yEp-k6Estauun`KX|Ai7AP8T@WD zy;1jl3wEykpqTIz|3P<_5+CscoenwkNB=I#9iNw7aP@0~?>bLAWSFw?N#f3i))(y{ zu+F8ELVgeI4_nlved@#qx7589HjLub0$MC=nMBKpw_Z28$1ut5vX+b406s0*@3%nP zj?1P+`=!bMOKQ)-u9i09HP$@+uy3#nTK}Xo1-Q0iNa(Yf8u1pC5_U6|EA2sU(QQU| zdVA3muIA=%%ffo{7+54dlAW2yY+sCxpy0#qb44V-vaj=yp;T8BoJ#YGu<@Y4r*KyX zv*jV2=+K;bHIo7UyA^fUCq2L|SxQAib_AMJWwMTZ?t(42+1B`D0vvo?v+4R^H-^2} zKhOHM6~|7+A5+q311IfWQkvhoaLXo{i%E9n=n`AapPBmt((5%0kF8F^0im&bJ|w^0 z;d{1ElY@fo-@9#WdkIhN9@ocY-Wfc7E`>S3%>z88VwyPwdQoH0NxS$c**{w@Z9Jjf z4=k@X(*MH(yfwo&dtq`E(lid{(MLpsMT~TFb|C3V9Q>s?^Ee6C^y(tTqejs;%v0}s z{WNwlHx9Tn4dX#OhK>O8-igqu`L97}5T1mx8ht7p09wDZLDe0dBsaJ)@NO~_S0-=T zv{jT7PS)~Ie})0@53*$nhV^^4M;aUakBMG6J~Ck2^1jwjAXfQJA(=D&~kZDWZ`rT z$f+Gn*+A2aOn&S^jW_$?)AdS?O|N6%hpKx1B{HYxQ2)jH<9!qg9N+s^uYmC7@5P;D ze?Ez?t05qmesHr=O*@>n(+!(epMdwuerb!mop5&Hdj5Vk;`iN~ zrLHd44lVB~YGDzwcj#3rja{Rk{#x@HU znSTa?ZW}JevQ4P_hr!h?qaIEwy=Ya@_gqt@6TbBK9lW<(hpC%Ka{0;JmAQX1KD??K zA}bDU7BA~T5AQUy*;gcgIAZZf(XJB|`kbgdR<;l%v^?FxUk=qF4+4}Y(&4|cHP!Q> z6i5(_l3*Eb$7r>8Zw^;Cfk{S*{DX-m#C=z?{y5FzQ0nX@aq|8tqUTZ6BKHTy%c{2f ztn#5mXIXQ9>JZ8}v7FB~Ab-C}pIKB-jX)i({ORtTKr|R)5X&`ffde-6Sqd!U=Ee|wCxzjf^=s$37t&1{ddQt|IjEVW^6D26IKRhCXzw=wY~6L<5Q=Ubs;3v zxBM0-{f7p7zNhpq{V*|OV}IJy2j9Njma6iS^iuibKRZrVAy>>H#^xtkU~%iMpWChh z(0;3}YrVGsw(#eL57Vh_#L zfpPqFq&=C9_tJ72hkR3 z?r+*;KX~+EW7U~2Fi5Lvci?ynL?qR6n21h7^wsOJbA zgUG73qCI3%iZZ;{%>CEXA?b-unRI^xawMGE6n~@{o`-E>Rm>r|?8jGy@^UDUtelW6 zxHbf9e$RcQvZ`QDGVLx=^)5J~VO4hL^mm|M<-B!$u?EwaH?t-Rwqg37XI_VhH+o3! z_+?Sf9+b^K=X8s$90eH#^y{xRjsgI(m(Y ztkxah>I=zvpl?Fb%C!;%dFCB`*J_cPyi&JeXB8&S>PXrz<-j}D&kZg#gcIiNI2*rA zd>iWHS9h#F!HgSkTDH<=;qe0YxqiaIl(%X7dXPE@4Wqtx0R}BFG+ig?PI}#=^>YEM zu_aKL`CnUKY!wF2urIEU^yBbR@$HA>I)GuufF+9T3pn?sQTPZiyun3bFJn**h@`RR z*M$=v5MRDVh+8{kt*dAsYnX@6pTypnl74*gz}=wZdq=S=^W;hH%1#_{ciMl6ne>}) zH%;a7jlqT=yM8sAXQJnuEH;pBhxrXA8gEZO$0Kh=IXricfr?dPm6q`g?9#UXr1d)< zj5B{_7@B87AKyaj+U_P)QPy^3mzzM9OgjEFw=hf|W-057LR2yQ5&iUg3yLo&1{Bi=Hof3UyBSyHDo|J3rL)~a-C>1Zfp$5XXi8Qf_JVaXd6BU2=Mq%{v z=k6+#??E)v}mnqb%N-WNgTFM#`0&WXua9jG;GtF_9Eu=QoJlPtMklx*Kg!>C=0 zG3~~@A68!pz5j)vRa zEO>|UfaYSdxl}Ytsu!LLOsd1h1!12(>E7UB@MX)N%~e44)!bGTSqBC8SB^YTY(#^T zKMrUp_MrYocBZ zoF&6zT&Wc7zSS_3I@JVLr|X?G?8v@{fla_KbrOev(=LWk#<7JVmXXhfoI_(aobZz; ze5Io=m=p5{(DIb$iF@Yh5MDjcSHW0=u@m>&lOnTF+l=-~sdES35*N_rxHN`=zWtZi z_C&)+UqgemX$l^8kLL?F7(?a#yJx=Yd_=2;QH`yyhG2q)GQLrN7_=wcoEYRs;SJyY z*@UA#Sn)YJ@P|kR#DrTkhm(Dod&BA9LVFWH($U?4^YJ%)b2H+L-ywUDeluzre5($g zgXng%!_9c$`I`6y;c9)8xhR&iwGO&N=DI}MtI@^ew4LX#3@B#M9r^aY2^^?OeNMuy zxM>y6xkQZMf4S`I>}(_#@ip$6T5Sy+l)o(;dp#LwMHgBwY#qmhocn`n&qKiGDOa4P zb2}dK-1{f9j~b3PY3P*R1oMwd1Q`x4Kp+B3!p zr5yANR(A559>t?-Mn`CKso0ZouP`Gl5-L#S9GgZC9_rh!vLalAJU;`9vlRzm>V3uP zcia1@YU;Zr`86IT3n``xB7Kl&J9_e`_8>BSFqxW?oIu{tn^QZI8}R)Zg_PZ`Gf>$b zA@h-md?S%&+m~-(AKTS5CR?(nUFzds- zL-e9wGwbnF*$e6a9A=??{c^{@<3m9AvN3&QQ7h2QJ9H+~lD5(_6m#Ng6Yje53 zB}O$NsI)XUX=5&?G*J!rOO;`(;rvBClGpwLJ8kBpr=djg?ua;Z3)1{6nn~>?9wnnB zxuEiX?D)1&Be9f&Z;YBOMaEQnN zcq!-W1$vVE!fc^}!R32^<7 zG7e<|ytX%KDprn`HpK;`jE4mITw6w5Y# zL3ex@^pA)9d|H`~)W^}0UPj#@e??WiY1arCwCHJnC)|@I7N4EoJQZ-SxnOJ2mj=k| zOdGzVK7i`D5X*zM*?&<|saxND{=jo-%9()+BF}f5+fgSVlN9}Ni@IHlR-d|ckv-kTh><*j2 zUu~OY>LIc2{Dm_uP;&{PW`$6{c_8(N?raqn7&y{7 zkM&~zdZ7qksd z1O+m6fSW#-bxyY{>Iy~P+)Vr)FQ(n%|7~wT>#tXD{vEHtT?(@{Z{t$X>Rm-p*oGEt ztgmMEB>SP7h-ue}Bcw-k(d!MnF%>O+j>M;gbwc}H!3g%nT6{SE(L^by8T?*7`p=s5 z#=2{~j&~C8iFM+8(JZ5w@TKESA3-f5VHNUFD(O>6c*{3^DsW!?nySxvpb}8wLC--5{ z`r19A-+7?6#M4OkwFYW=gLjBN>Lwm#GzzXJJq^tFmsWl|^i zW@8sd`{W@E)XSKDn?D2{4V$#&PK}^smE`fh4Qvi!ci%%zg})IpnY(mJ4%%bTubJ=(HXDatF`4N@uEoy8+u{Q_RB!*! z;YvNmq}EKxksQUDZNlKWy$YkBu?i>fJA=vJ-6l6*QQ%rs_+_(zdJO+GEy1 z-2REz;M0wxzHF@@@K4M|DIshR$uq@j-adSj_!ci-SZH;wXoQn5zcVw~cA-p!lz%=S1%H^>^lS0-;quM? zw;un<>d(W%^TxX-*vZ*-LD|0t8LsVg70w?7kG^xYopRkUSbxNs%b^Dk?1*dK(b0@a zrU}&gV=rQEgoUF`|s@RjIxPsjbC|A! zORo;x>+2?5Ewkp_o~=m`@0D31aiavQE*+Im-PVl)))$vHSNEc+k-<}k6I2vo@g8d- zJms-;#w1ZXM=kyjDIt)ZzG-}<6uK| z%@3upT*bjN^tu8gPI4VCBizEa2WqNM?$;4tykUobcQ-EmP;w9aMCMLmr3D8kYHb_E({BBc2r&I^L zAzKqyGe~_`T=M#HyEgFgu9H0#GKl9))f%21Yr}tMJ{Z-IJ;!anmqxW~1NcSyv8&Tj zO>Axv8+`Ym4TE$u1l;ZVU~=u^?LmibDF3)so>DswJ-gog6o)K$xca2E^jsS%Y0!jr z_hq2Khnil0&S5;c-E;LxPzL-1$9YM!_wd9@53=Q|;J-s#_f#g5eAu23SUo)k+TD8_ zEJgECm02lXx~TxCcYd)kkL*V)ZI|sgNgnB5>_d}4^A_ZyP3aunHHyvll{*`i;z{4~ z;h}3O)2PXu|CL{T9?u56ELRGu$H+d139Tiv7ny17IkD6UqSsA~{pmsKXWtoaZ&ICb3(^j!X4~HWb0ogOCAbyVUV*U$e?JuU_IdUo3 zOuXAi3wmasET(}k|9$a|nS|ftABj6xyHF@hB>8?%4e+9L?Qyzx9N9!Na8^y2`n`ku z!J#Ht4ohS%i%0;!O?wSy^E*Mwse8KTb|-H8oGxjTl!bey=(fr`QBj-kuIV>(Dy#~e zAN_f=lk|JS4$J-N#w%xE=qIpt;cewR9|JWe!0eAgl)FMTe*SwyO!7uKT(Wv<^*yQp zpPm%h#rq~5>sK%C7$ZI}_Z$3A4CIIxIpgh*4dG+pCcj>$LHZeD58GAtUFw1zx3|9w z7$)4?wQOa#m*yCI_K5e>HS!zup#`rVIA~T zGqy9lD2IeR)?TsFc9c6)9#x|79_p3%*0j1;f&KoDO^>u|kSWoY60Z3k93OU>)R7)* zRj-9bj?^>;e-=5Cy_JG3kwuo`nk2v9cQE-`T`meG+6u>eje??*w0X8=JM?9JJ7q_5 zrK86SpQ#?1gelv7)#X0fsI0h6eK~q&+ z)<(V;CI4tz{_q~g(A0}Z#Lj%e2@`IgE3L#2bSZq-*-ryd05oqRi1%lwW!g7=Z^FAY zQN0$XJPH@YZiM#rHQ+g=2pj(J4!jeiZjhQz=GFW57jIY~ew6j^qFpnD9~O69btR%7 z{{3CAkhgBc!#u5p1(Eqsr8c?W_MsC$;eG9gzTU>NHw8)m!g zNe=$cw)0sFli+9KdF#ExOX#xvR;VjJ1e;Vp9#q9jm~Pu;Nz*rgD{I<{j>p=7kH%?h z1M>tj(73Bub(Ubi9qYLo?Ga>@pPCfr$wm9!OCBs+Dxu}q*DNc?3P}BHz+u0+36jKL z$kJUQ&zF#W7orZ-0&n%4UVFq0@nb#UtX9p31Gj7(b7V+fVTe;GwsjizyxHB6E*gKI+x$WiIef4nss?WzKxBKCc=|L~o)FL#w%l{}$oA4CahcCz#Q~;}zF3%q;~KO+4~#B`30t;NK*W8+WS)fN zE2jw9dFGa9Ge_`r@5At4rFm$-rf>gbyd2Db8f#DPECfHrD3{BZD=_i=@Y~dyaag;q z;kp=DfWnjmT*U{9FuwYR;})x4G%#;_?d(>E?J{%{Ee|u%YGf#)^92Pw?(ezbb9Dp~ z=Mvma%SU0?CsqH#&4kxnBHuMvY37dDXGcv^labvOy`E zy}ZwF9wc@cYCKm?N0-FJ;JB|;5G92YddV?#-uX4k+pz|t^y;tCSVn`O#MyTx?Rjw6 zjEA9}^qnvJtDX0yD}fKv9Sk3T55TGT=yyK7b;JiL-@{(kh0+_f_Cz_4;pTS^vxKCN zCwt#>$cWd1vh_`?gEJrC`g!d+Y0^i!)cO3vS^q)MC{HWVbI){ftg(j{-ybbxC@ zWk1BGCg84j!J(bQg`hjBy6NqP4>02vB)&?{NoL$N4Lx4ocz2Q8a0lTNuzuKNU>N-w z8+C^M{Alb(D*I&C|9v&1ZI9LuKY9m!bq@xP-6vk!Wroi~uH_*9%CWVQeC`uI@BbVl zTpF7%4>`6dk0a|a`))4l8t@JD`cTY1ikow_pYVK;ywqn!^gEA(UB8IF^ZKVISwUp1-J^&yZoiF1hES_J|b zN0VKP=TY?0A>QrTGsrXJ-^lPf3@sZzT(SPzh%;e1B@7!AJXazGZ|=B~_wfSZ zzS5ej8FzM~QM8H+o$4g<$S71w-3|vAt!|w)vwoC5p{X!Kmf|sXE5c_}-+`!occUbI zF;3e$()%=${ml2Lc0yb8FqwPoLVQps{18%D{O>#!Dn#6DN0)lg+P6Xe?EYSeju%R- zZ*KwGAj{t(%N5}8V;@&G+W@de7caQ!kn;dvrtq2HC3ri%lit_73wjzZdjt@lVE($i zLCkUoM&I>Ii9gT@lNEPPJR2rCL)yXb{PY#@C_XB|lJtC+lB=_3rzj}N@VmXeob;po zbptGhW`J)=?S8s#Cx%@J5!<$_2Cls_6ig@@g^cwtmrEax<7~i_QHJSe7=JFCONlB3 zhg6O2znKOxPy4|ZyPHF(AbhZbKZBgt4(h#rIMRXrPR=<+ZS7c=K_}Y5Gz85JW_3|P z<6!$U8OB>*;-l*gdWTfyXwmHz{R$_j6Z5o1vOOtc*vhh!}eV8!eAuVK3 zOIDdHVf0dr%ejmR;(gt`iSAeo-m80Rczb_8ga$|}slE(^s{GkznIhulRZiZ|yeA$) zPBT6_-bFZe`CFcTAYLD3ouWWB!wxXvwrgd()DDMu2F?_n9!ImdM&*a`VSqukCz83k zaPxVF^;yPRFlzRk+kU+Rr8?KXwh}&Z@9}dyXI?a6-t&qpTT*+Fe@W-N-?kCl(k;A& z{c9>5HR68|wr>jE%Q!gqT&RN=CXXYZc=e&|P_3=SzXtq1;(EP8V;px_n{%A47(-Vp z3jfrU+CS0wXq1xX6nZFLn0 z1X`%)WXF_-C}$XfXF; zrG}>ikKJw9U&B&~u=A6ignA62(+|F0H8+?yNC3728xn!=qsig9Jy$a`*uXmPT$r#_l`{PM#rl_ag`|qFz@}mCUOT&%F;XAK3mrfSfB!Y|za9ux+Now!L-r;abC8fr-U~ zj?5z?@O8<%tSyQH{t3d7A*7eidT7s^RVT8?I^eZKW^oSWf7yxF3eH2X2ZtV4vp1sw=(P_(1&F7-X(?cFZ=7)Lt|!i?Ia zz#fOY*BCYiHRnQgLHt9VZP|Fzw1)HKZPF7<){Wxr7(@{@<0IU};~b{{_TRSFLEJPu zthMOb4fImNA&cS&Rle~x<23zni&tabhVUhMH~NpgZJp08X18>WCT_-d^g2!>_7{d-F2-6BY2sO!pq9kiK}X&|9-yD$G=~j-dgP+ z$BLR1mu#*@{4$bUZu^JaW3TV=uIJwyk*E-H&7NF)5cM`Uzf zS+Ml(S`I&h17`H94GoZa%zyJty!*Nb@#F^ujoN>0u&^L`tZq{+R_q<-=o2URFQqen zdxg4@`>timop1^q`LW0ML(3R&SKX`3D(oV>H14w&`c=5(A3Gz`)dec0BgUzu2iruk z&-Ci3#Fm-sNB)jClh4hH{t$B^91D&59PvB@<_mN$`);kmH(Lz2ht76m^TNyL9o#MG z?C|Tq`Q<#^cSfs5!Kob^%&u_hm<*uRXlc}gX0p%SA@yTQq5u-yMe|At2P!d6t)tBFQDSvC+NFq`)~XbxNyXD)jbyv_VST_h z-z~Qboo)H9T}mcBwli6K=Ty`Uo$dzMF4?`A{dC-ucVWOy=h%&WUaVb9G=Dkn6qI zdjQT0pL(}TwjUJMr@C`hhVjMzH*o_39dNULiY?Bo6Ju{QN*qx1L}lI1{>lSkjRjGvV0$w(P;v`;}<*`Z`B&I~7O%1>f(&NWl1vO+gmj=*l`<_cy%; zF3x=A%b+PiO0UQxZhPW|c%6~_eXJNWd4tW45I&Z&QTXw&pS`dikg;8ebpkY_o@|tS z)&!TO&#Uet`BG8WyVOUfjnL`eZqh*bP0=jMUo&e9fqE{U+Uh$3JPcbW=@rMo^B)Vx zicLRob_hIZ-S3T~BF>@V_Vqx&Z$;2Hp%LP?M=&x{vVmH>(?|MNEK2>y7Ppt&-FnfU3Pvn*7;eC_P#;Mn~oj<=FFb@JT$z8kl?jBKO#q6I#MY+S`zoZl1^6z7yIu zIZNML&qr&CzP|`=BDu5A6f>#6Nt5uDp8HS-S1UB@S|-li z=!WbopBT>mBpgmhX6xWn2vK2F8)Jn^?4N3IO=gV4M85Zo=b8NRtcUpWr?L|Gl#^k@ zd9?s)*VDAy!`ngXd$EtFVKwG`d+^}ee}tFWV7O4ajpQ|YggB}~I>9|_RkhEx9Lrrt z_&)NGdnff&x(zSk-FR;;e7xm7+*{i`@XCAvqIC?_X6T0?}N!_mG5A;;o~m3G}_=%E8bo#T@UABghl=m(TyhAm)o~O}XUcjJCSY+aLZ{ zi|nfc&$nlG0L@C|qtnJEaPye|`C9V4{+V?s?LkyHRs`%bTFGF|#_H-fLqqRl4>?tsfzbnLcz79DK^%qy|XuPa6}oDb<_D-df|*%RDqVD8A(4-PejV1L|YInAdOEv{4_t}+WT5)GwbHysL@@9NXp;9BEyF(lO_Hv!}vwUEZ#eq7n_S#3LmHh00N2U;JV&d0rTVD1A^8 zAv`*t&+lvh4x#mJi96~`UC2|^;&Von3b8dEzp6~gbN1ef-ToGTNF2)_xDwh06$?*f z9&GEyk;6VR-*!x+VQhuumBj`Ox@vQXi*QO;6Lr*C?|nv(Sku6qzkWFMEkGtBh~$h< zC@7D}1VMc1b7xuMOjHtb)$5iWgBbnW-@*uYPc?5mK_aOS==wCTZMWz`{VzesyLvOw z{&wp4a6~ma5QUs#bszSxsLZ9xBJM3T;F>P>!>Qy6%Ef(6xCU1{CHMD$saD?qC_3+O ztlu^c7lpK>h-7C~!p}&G%PfjAA{t0TOIBtn$|$R>jHK+9z2{}`_1Jr_iX@3ty!ZQm zhvSLoJMQcHe9p5ef^gKue0slLHtz!8%9Ep<5+!(HCAamCcrn)3xL9i68^k1uQ6EX& zX{0&ysVrrz3FR4ib9-kAXLHj(x*DB6WR_M;zZc&DoSUZUW`&xtoNbKu$`Rs0=9`n; z5l8x%M}qyX($C+r^i;$+GYoliwenQAjbauT-1TB>0rsAM+?+Ex z$U-~7#dvTOJ{tSThR_yai$m8x%k*)$*Yo-s=SCLtM$+<0iRL2Dsu-P1SEX)#|6rgNi7XvbN<61TlZH=4Yyg7pGOeeIPlPA;X_R z#|cmAo!mQ0pQ|_29Vk6rGj|U5#El$2U(<##vtsvYcO&jRnNT`gmJ0=~4iW#B$G}rp zNd*aC_Zt1{;Ns325MSSXoZYPr3!QRSSE2^-jy6>s(}!wM`+2kATfZTG3t*1jXzE4n zBj4n#i7zZfXUSP9DjSaKJ-hp#b0x-H+9~#wryboY|0)e$3Ibm5n?;I$tAXvQ^Rf-$ zxM+R-=IdhJi50I4E_xjyyq;mIa3=PCoXX#-h~sI%l~q}_nQ+yM&#ThF0tK(_)%bL5 zc?wRhow7I=QjI#MqCd)fDVX^Aj`rn??Z`9w^ac%EA8;Sp<}1Fb5|ZTP!bSEIPQKAu z#hlJ@FjQ~&rSp0W@)$WU7{iu!mJBMnnlXFRF zv zPk>RS+eOUmagg>;K0<#>4{#M+rM@dP1s#i~8QLseu%^z(n?`c*J2Jnn<<12Iqlx2I zNj=irn5%p*={^i=p4WYgB!f}Yli{zDOgpN~?u^<|(TIVSgVy`6cEMpmJ=tHqb@1q~ z3w6V>9Mq*;>D96t0hef-RNkpxusbw1!ycH8^EC=m)Ew1tCl*Imh;JuxHQRqr!x+}O zY#ullm5tXJ?uGfg6Tbn^!u9~d|DgXf@8@`^55)TSm7nPCC)f3bjfcV=__$GEmNuXh zf5vW^jpS)X|1973>G_W&x2HZd%h88xW`5iTr^r60Zi!BbaHP4oJ@(9h?7$*!7C|k? zLHPc6;>a_(Qrx`7`ucwk8E`C+BPT}334T34?j1T(0@wa>SZtvmLGuQFE;)v1JkR^w z^e#CUM;&#m>vJIf(LFW+tR#1z`wg<%#z#OfG-R`x#{lwmpBD3TuEn2ah3fmiWJ2yd z*GFIb4q%ACGWSKI9IMN9?s8t~!7z<6pM;y_dwK0b)Xmja%;t7w?00_!6x}b^ABcA& zN`2fS@wNsnV*eBPV>pOBWe&Jayb*i+tyArleX&_U#Cz+98rWG-zgbM(9Wy@EKW|DK z#O>lj+Mf%H@rSdcE#J+2_%hECxBVyu?bXF%GFPYsQ8s-eI9L~iDlHnLw>5c!cmjMPoOuOc``aQehq>Z`;rIr>$c=PKzF`9^Kj zkIgm1@QNheh<`WyDN{due9#>)nOLry>V#ow)F&EBQxB3kuRu>~8*V!nz9b>j3&SQ2 z4^z~7v1LxDth=canHQw2h0bB`e5E1XT88&NP>y7hxr&m>;$M35eKaYz;%+G z^~Mf{iq4%uyE9cPhn_b>wuvH*9S`BypN>);*6#%`%c}aRrY>Ok%t2+9LioDbWx4eo zq;DsmqT9XFgQ;J7G{k)>fn(=w`cy9RcaMpeJDWQJvb_>l-Vu(FFtg?AM{&ZJ4P*n2 zN0nHd_L}}@`xt1}_zRV_5Y9_nspi%{%}~%d6<2$*6V=*$pO;=7L@`M*>MP0pc#+`{ z)wx}yr^ISq?pgH_-*KFh=oX&DK}F97m*`H|qpdDnNb-l`3pAJ9Wd5J?p**Q>8-UX> zzia0+#_&Sw>uz(GT9E$1_DW_C;cGG9Ki)0Z2_9UVR8+!}AmKZA;()^_e%+~~*M6xR z{EV4YoVRyj@rlKicEvtWNYjOKW0GGO{$t^~eF~#vYlG|BhHxvBw?4m2CGm{he!b&o zB5wUhkS3%*a!9sLvqZNA4$*~6?V9QUr!47j(Ka1;VIfrSaBw;r|2ys=YDv5#XLkGi zBKvuo)A8Cq?7b*Q>C=m|Z$&MJ7d+3}Yw@eo&CR(B6=3Qbm-}Ns@gd!K=B6}|f&BAl zcQeh^z@@9^SqimgfY;NWAGt?(s%&c_-GU`3TYEgq#=8^F4R6!r>K#YZx4*wvv=Q&A zYxwxnuLZEfZ0Q@B)k78&+mYT03Vd3);Y=X*U^K90nMzv9PTgj;X3Ve9jqzKOfemfB#(vc}8wDcW0{M#CqzH<0e_qV#lFm zWmSVdm%_e`xi@dVe%6Uw?SHKX@Azir%-e49j%Q(DNdabtWsN^Lp2wo z{w2B0a$8Q*AhVy~%{?-bEiy*@=4Bmh@^AyJ;Vtp>M2#`wF7Wyn4F zn|{Wq9{e<~*qCng!04@C`a#6&tawDi*_q_4PbP9TkJAr9_m=;r_A-&YZ1oG(g_bmk zJv+tc%Upw!hTF1a)5tm5sEqZfJOxuy1FK#zkAuj|S4Vt4OhQ*wPtL;ScJN}YOP3?_ zo$WN9w8}5r;I^Q~o!?{q_|o-~#i46e=+n-+-7t0tK7H&|Eg|=bu?iKf*Y7i7lsDvq zp;|rmIcQ0>ACJM9mRWaZ9EK;VpFgMe#9_=~ay`;?qN9S?aoWGcd)hUkzelGL-)`3U zw#Sd~5!!C}K01wHBA(`Uj_??sH&|MTXLRGb?vV1|CxxiH(Q{PTaR9_O^YE$7)S$}M z!{-%4n}O}0l2&U-Iy#JM8dlq7!L1-`-t&}JwDQT|KV?tm;mZ^yH@!+Q{yXV@^lJ)! zdQF`*w>l2nocA=9wtoS2bL!{aq>p}&dpEu!=j(cfU}}R)S0V)Rof zkfC7Q%vOzlx;Y%X%^&__f%x)@UOrkQKc|%GtH+!C9;oEsl#^%Jh}-zrwEFLj!f{be z=0jvorbNj%cIhSW?SCKZb`by8Re|vLJf&?=bbapR6^SX#s2{gk?5KsH>POb`sWC{V zjeTXe6X5&V^mhq&s&M`0;+JN-S-3F1x!=Zn4Cr|iia)&`hRtb5?G$f~LE)WEzFQBx zhhzUP9z07K$Du3hS^u4$LN0pok>2PWaORB>%FHQ(>&j`VvIwXh?^`NVU`KB%f} zIX?x=YJ0uPYX>2gxk&$_*cABnX?LIH8H0=5*D2&b;CY|CZ5;kZMMxDE zgh85+6Oi4R2D`?Ee1-)pfVVDtN%jlLwd&<|b~=)|m|GX4cwr;Bc{^?$<|lj8 zUFQx&9`Hh$Pc+=eWe89BnZ_QEC!-*AJbc!X`23dsURO#E9)zIEY8naGGV~ZX-L!Y> z7;O5@`B9^%5)Dn->P2p}priT%jJd*h-%e(jtf=j}V2 zC#^@JN>zVHTTnCJ`T3eB=T$#mP7mE@+&c;uo5{J*QpzR+T`LG5%u6kqMC{rQdLXvVzPX(W_NL2uW$-@FCg7_74^wXv}o z)XTNn={0)c@lS#5VPTT{zW8qWxN{W@E}cjmAU=U{JxS6A=?BRl1y6;QJCPbsr0f6c zg*G*>iUEls4Bpt-Jalgat3;GudQMlPnU8C92FnCQJW04bBUS+&8vg`m97mC1kL~E= z{!(bx=6Vp5m4jD=6np$g-oHbPBP5+XzfKCc-x4=2R}8&q0y}Z zijq33Jbscq)Tike$y$D}HK_MCTgn((m(*#!UwDBf*+JP)#CxDCAw#at{-Z-R+T-U8yR$7<|mp(~Pkjbr!S2 zex2*V=l#;sJFPh0LE0>9W-%!6hvIJ3aHQ2V179RFmTiUHy$+$I_o=R|?< zlAE!Klum4!vgHjvnGJSVO@#Q(hR~%sqwZ=Z;d#;ij-~rl3smy#&Wgl0$^P3(x$x5{ ztQ>Avm|CqP9>hVJ=GZQrt+?G4LGsdxxPrZr_FFhDVeEaxc&S(b=JPA~~HQR?9 zEb#+Ue4Xg@^kekCO2Ui3r7U{|Ct&>L*`{UI4s6MgO6<*QM>~@XgzAzA0$%B78M3>; zUc&7uf5{kjnLJ{7N$G&c6E1vf<`guH{-IzHIR-MSzQ*~?Bk&_PAbSbRamU&H9N%lR zk;nVEmR@5Xc%IId-;fyxh3=J_4aIicwMxew@tO2*9vdC?pliaLhEE8Am7L%6#C15u zIJuIl$(<-I1Xd^`DRWfHt#TrbZ+(g3%Qv=v~#)A622UM@svnrUs~^ z+WxP@{2OM>_Yc~Box<~*_GWlnw`1vp&Rr~I->}!xX?f-1IQDK84`U6?g&s?JIh#)- zpeJ?Z;*+IVSi1a|iD3^ya>D+V4&o_iA7}lddGaM}H!kmsYb?gDlXgP$L)92nUl_ml zHQ{v74Jf#D58#K%r^^-lLU8(g6N^-54Nexwz7b*`!mt2;rfZ(obUE440mAP1iZ zb-dU%pNRSTH-bv+-{HyY!l?{V?ZD>xiu2N$QuHc0BGO@-i)SX5sErewVVA>)z`)~^ za8mz$QHO0CMA`hK73k~0b7zDHn8+M_9=#a)-nKz&7VF+Bqg1Sjy|%WQt{z{UJ#bm+ zcO+Im67}V_=z}Wi!tkEQ&FFB-RsHM31T5h^?M>O2g=4**RjhNhsJS?$XnUgwUw_q1 zo7vutp1VIXui6qWfh28q|4A}OpnJ{TSExz2SFyXz>F=Y^8|pM3vnsIT-zGa7*$KOi zHx+yv9DsYJcB-nrRiJE|xO>UnpS({UsaC9M#4`a-ew%p6-;rG^K|P-I;I8jq=_2Rj zKq}^$p8K_EvneKLA31l44J9&`&G#bRwkY3k%%kwPyjfrRaW6ceFLrbOQ%(A)+b+`! zPUHR+Uo}sLDKhtRq&TxoBQGS&vk~6F{fCC%M3bsfQ(pLCuXHvzZRL2fzW5SX)aOlH zc65V=`wro+sub+In{o1#=sc!%9Pe6uK1KGP**7-4Yw%_dEe_nv!u<>~V#2utFu$pS zT;(B9dVSNewHY!W(Pds$AMb|ym7Qxlf+oNy{|w#oWx~h1z9)AlWeT%@gk20Bio!#W zPfP}UNCD%+4N`le2O(@QfH&=O6Lzln3yAJWf_6Ev0Kt##FcNJ(82CILWK`;wHlDRY zmbHAI{q=OZ= zxDH1^n7!nq`ekyz*BqgUzidZUs%q=lK}FYH>FL=H}qGkWyxz~i03wdHr#uaHl` zyG3W+D%%T{7CE#3lIPg>RQEj|49zGWdF39HSp^1FPnXokOu_Z<)S~AZ$om^>ood-^ z21cbGVJ`FQfIFoYEssvL1GntahpUA9_0gtITBmLbU6~byg3J;9p?-vuG4+lq z-|a?>GB3=bzheR4t2p!2?zh9*j%O~k#GCg<(^nx_uHnX|n0 zZWG+;*~B{VtQy3kU6p6sH)|BEeIYstaT_KnMMio+EAiqs( z*A~+6*()$MLgs3{s_%#YsgHo43k|33$rdzu+p$zpn2tvVEgt%4B%9vWO z*P$-`1BX_DR6Jq)bD{5E2Ri*TsrF{>B>(Tk+A(@^{_8VWoeqfw(I?x!PIQ-p+KFP1 za`twR^rrP52`GoUf(^f|RoUox>)q$z3Bu_Noyp(p*$JyHk?M9Gedx45B3Hh+8gh?@ z53stF!xWt>`#Xzn%nb1~jt?0|2G2R+si;m^@tepP6c7u>! zVMy&plMN>80ta?PcEeQhW|lbO?G?|HKOf~Xj3xmO$~bviK#%&&li`4N*tKBKZox>r zH80nG=8-wzp5z6Y@$*#>!}QxMC%6{-Z^x*NHtEfVRDc-Xc2|B7<2S0V*mv(>7!WT*N2C}|gaP8~X zAJc^IAuaPbnoU0sd$>!-bWJEgCFJN3l3$FwX<}v{LMJ@3*qr#^9SSfBt=eg2f5h5l zCOPwe!$9P^NjC*Tz#?isQLnoZiXV3zN>CbxC6f#MAnLhh}Yf5cWRfiG1^*99Hg^R}X!mfX}|t+l5}VdRW&kFxTx#)_rOZ5>13Hi4e0sh zL_QoM-``-mlpu0ARqPYV$@@3}*W!_C+YI66Itd+E>59b{LlWn7?b}I@msufp9`VJA zVU;~o>3H_#Ocu4_{z8`hePR@r=ey15(;q zT(H@l+|vs8D?nMrI9BXPCf+!8k@^2#uLm})3HPgtA$e{>p5A`~jSI5}ck<1;jM-@)0LTNIK0C8s6M_OmE^p#Z5;?7MkKj1Km84w zT(Yx}Cg&rab9}~oBg>)Wmxj=8r(S#;S62CGwF9eccR3lWrXZf&A^cgnAMfS-^LntS z96H%rhKBuzVN>Zt+luFe|IGXNJALUG$b|>}cf*ogCy~Lo7Mp89p6}wi?!X67obOMK zBm5QVqO7y^A6nqfZ&`EvR|XyyPM3!Th-Za&bnepAI_P?Rw{yj49CuEicv7&l9&IA` z2}C9jV@rcX($$g*Fgm*YtCgl3#REKF&8P%`=%ns>eXSF?%NN&5qz+ zp5(AMhGbrK{O)S&XTqsT-O}&CIE(JRNwOp4zPx3AUi_3r1xRvS>P_11FR*J4hJe5AiuisvEU-%)|s&V$K*1EMRw=nB=6)y^?s&v zpEf8UG*mElhxA08k1>a_{)agF>REQ)_HiCCZqtki9#eW(~%{-TkBlOR24R0lR6ybLHu$69{;X6B*XIOyJ zCsw-I69&LjY@;;Ge+b(XCP&5x>TpYxghG0B9f~}@NzCl#SEOWBj3xlvs`D3q@CdCQ903?m{R-_T=m-4 zhHw~FPgX8Te**t^!^?;EHezgJS0yz=Hzw++>=Gw=vZT3(17EGre2^+@aG|;704`VQFuz8k3y*@1C6)Dz;>2Gt=l%)Xh!|* zoVj)+-Vumtd3Aac)-T?HWON zXi75hjYSW78TRt)%yr=>5!~Ep4&63IA~gy}WX+ z6%OjKlu&|)aotEE)2Ooy4kWj~(H_l3Ve^tJE2Jm&TwZBKb_eko6?;+^47=f{`h)k2 zbS=Qq>Ce2tUk~Q5S9S(<*nfN?5{Bq zX{92$YRN4RXuMM3x>)_T#m5COqi1f^5j+LwQidqcq=*vt<9dX#bpr@ln23M8){fWm zFH%aPNN?4ui)powLUQ5v8gop7z|cJ3=1&hfpGrBh&;$-b&54!MDFPjMmLYViX0`{^ z_{-!31%}|cgGk{0&EpX1+Zs4R?h7nQZ)uEHD#7dUgRDQMJ*b%cW4?H$3kFiWAIcU# zhSratpM{g0z`!5;=j&65J2d%gRYCS2EeE`^>pI|1*r-p=4^#1IH0k-v1N3bW%FWZiv~S95a= zf}E{y>kxmJ?64_aFrF|Q%Ro6=r8WZ^uxQ@N#hV%%BAkx z2RERNfjpPk(pw1l!F1`NbUz9_wYntL*@vALrxueOYGM4C@N1f`ZsH4-y!lzT8@Vmc zT5j^HfoZeuMSe2Bc`0>Qxnq1BW9I9dtOq0D=;DyyX4i30jc!*u-_i&vBa*bikNZ%M zOGA{ctP@1?nTYOoaGeV9$+r}Dy~vTc;EX>ler1EM*K&Qq{)X?D5lRD;P-9C zP+hJp8**RESZ&BUZQhJ;*eLb^k=eL8UxQigJ?W83`5t5)A{kR>in(Yq6F$h7<`%k@aR|QQk`JehFnjIc?=QE*Ao}g=rt{zOp`4EG`Q#PCy<<|O z-c9m=E1EGM=N9~WS_{CsjQ%o0Lw3Hm8A)%Msc}Mlv{5S1X0!=-*%In@LQW^=hw&> z5oNMU@{LL2O(No{RbXEr)%{Uv39!_1C+@Zn{s+BiylAQ>DX7fy}Bw8)$` z5BYt>Yx=5fDKm$ZuBd5e*)i-G$sZT@Nr8s=hwfcrL70}9Dp}xJ0UlF=d4KiBAuU4g z;&bAYENX6G4Hs(wp9gb=FYR)1qY9Yj5-nR z%+h@y^JjXvio>gAUkS%sW@4pdQwE6Mw~*K7?*{qmMkh(ik9V9n#nj!w`RT&E7( zn}upw{*M)ChoS0RZ|iHvVvMwGIV$s{99!zVzHaJD!SX+2ua?i}p>_dRk@%?|4Cmxt z3{;Ls3BSZ63ml)o_ZYX`qyB7Q7?7K}JWsqy&7RFaw&#NK>KE1jqzAA`XZRQAKqh4A zt!FVgw?j>);l&J&66Cb!uYNn71jDC>J*Px^AZ^b^#mQZDxSliA8F{%DQmE4GV7&&V zodmaRbLzkY4EZlrL_a~rrF8cQVZxcQDvx8xpTKcz>edR83cR^`K}D~d0+$<9ZqNS7 zLo-3iCubWX!AmDNUG^<`-}%v3c7bsK^MdUEE}PcFgNIY&zrIvMYNLNIYXk)t{yn=> z$5jbe4hfud9g2X?;`37M{h#rhr~YHm7{mP5P@5yuBczvU`OAQ-3=ZEsldVH|%+!(Y zNBi2#35UMBVTt594;K1uoM|Urg`!>qx}tkv{W@G^Fy5rde+|{77zL z%WDo#`a#04HeA|5O+0c6D#GU~CQ!$ojdkJH6y9qxlV$OWMSqXevH6DMFp+DxNtmM% z@Wu$62H6Wd2yQhneAb7bFZmB>nstHDS({+h@1)Oa)#LQZtQ0izf|!TMT$uj$bPeP; zp?B8~wfiegIHgJdaPmLWR}lZFBW^PRx3+GluozT>BCq}yMbh&KJ*Su)NizH{c+L7w z`BG&LlK7RMG}*&mlf7#GfvE@9y}0`po)908&T_F$7V!nq*!-k=+7E;7)w{Gw-paz_ zfeSrHJ6x1?b+NunJc@nog$)V)7?+v@Q|hx=K0bd+<3$c0WQ{4kVoW$X$6lI`p{0=Oy1g4ok22_x`aZ{rmkC>ryeo`%M_B`my6Z zSTXDWn&PPihE7A9FOpOE`9UkA_|7rB9TD-;rC{?easp2Av1r2xUDOo(|UsiYy=1LM>Ts$QE(j;zc{IH9Wuo`k>8 zV=|e*wP(%jqdB!OcF@GR@kk4#z0vTazB2>mT(PXYR@r!h*%)&7&*F>4#4Uk;>fue{ z$>1*}zw+Kw`ToAB61+|SLu*lC1phPqZrC3mje)Xmf5rD_;Y-0xmy@@N7rZ~G^TCxW zWcamEu%7i1G&u9TuTds3s&@TEU;Y~$T3&LcemDeoH{wL<^C{T1QTEU|z8*T~GGvT* zXF`GhxVxQl3QUB0->nObK_w-@P2WT2F>Zv$^i#_)-YwVD`nokA99g9Np4qg){jJxM zMV*Mx{f~$FdB=7Xac`3?<|RFp&etpfPf4C)&;HyOe)VvnMO;}uyb9Y`I4Kpp6C_n` ztx#Xz4ho85w;tMbfMQ|RnZm6z*cKNcDX^ywHErm`jW@>N8lT0#6Ox0FImW!dBqJPx zLSlVVKT{xXJx6(6j^t`76fv$X=Rq8U{dU${>3hQ z(rG1Yczgt2a*G=9b`#&CKp^Ogjldno%kEOoGr{1+R7MS7J}Q*2aD7 zQM&$N=u_GOJn`;V;$!|!d?Q>*Q$lHmgp`1nYWMQ-x)e`~+2UIa^n4mYv)qG>vj$qL zwe2{@ZSQmcBSG-$>t%HqkR0TByBtR8bWq*Csp+O;6`T!nWi+X2!FyJRQcu=4!LF6s z{hgnR!Tcp{^y-0eWI5<@^-V-F){7q4t3qjmT|a&KEJ?nfzCq;4%-1%^KRs4NZGHn& z(&SGB{#T0$Ijhg6Y*WcxS+kKnrVai4XS?TI+EF^=UA5rNW~|@$`sd1rAUMx+d4HEd zC#djwH1))0!IzHNCiAfYn5F3ssUtq`bB@crwJ?f|6|5O-%UzJvl=s-`U=G^F>_6Ta zLcyH1P_Y+hvO$%%iD6Zv1)Ph5sQOb!!E%k??rK{q(9`<9>}@N-evU%|!pZ|!x-a9+ z#Q_R5i*V1TOjg2!w+EPKnta`K*{dfDv6~d>!sC*CKwLnb*tFO&W7gkwy z>T%PT5nrfE>jUgTZ63Y2)XG+L{pd$^L8T2`C3nmC-5o`tOFX}Iru%V3Bx0-g6tyuZxrRr*f=h2jnZ}?W8vB2mdPt?b)k!Fi`RPowYXw+N`TfzM6KS5+jq*caGP% z9C4qjJfjBha5<}89P0#;_=4sqR5PFh6!|-6KEo_??BGeCZuoCFtzjjo3)t@5l_*r{ zgS4K5>^I5WE;-R{geLMOw2$RxzS>7TV#=ephvX8WN2Y~d?m;E?JO2(lSVjRoz0B9F ze{xapih!)P+8o%F7OX96H$x%!f6AYEN70CbS3~VU6&zZfePjNj1LyjJE7|PBi5IX< z!Am;|4fi(D6{TjwSBI~+wshu0^MRE&%5ekWw#5HWF5xrSA9#?tLO7*Hwk!$l z{Xz8X88oDI>qd!1$?$ucAEAAcabICXCrs<=Y)#JZ2JY&Zwip!(_}^1kTdPk)fi27? zf}{uG{H3e5k>oTpXd3_i_}K%!-OT0uNmURU(e~$UGC8LtN=_(C^&?Ayau#28CTe`z zu6l&*=O%(D|Gu^_#ju-u2Xu;KK-@5O%Y(-xXDHD$Hsg>5elwYw@^wiNFg?1SlwXe; z&(BL`wUhjah}T-e**`o_4ir z;Cz^}!yyP!`cwVbaC9Nk>59-ONsd5uqP-n^2_qAqm)W-LKeSfn6@9T0nHbe%-8dhWCNBZag*c$1dEy8(19EedS~u;7yQaP}j9eyqF

aSv`+glU@qxrUqHVZ8&y~y{PHSGAGfBqENzcG!!qxNF=C<8S-3o7>+x{qA9fn0+ zOQ(PCB+qf7-FS)gA+GJtY`VQ%f$u(^)McM)g@t{|%-gL3p<~;so}=1lR9KKSVna%*b4UX`PRT3yI1=N?dHy45^?>l3&$b9qrfvd2jRAIP;UI4~Om=N)3cHVWzx}afL_?c$K=eN@@4P(EG24 zj=JT6@b-NXI(ta2pXPbkkBKI%H6BlsoUet?sTEG?{#h6yB-6t_5{pkba-#2Ojp6}y z%QI5fdqI8+qsb!26j1(Lw92fCfm|sY>&>6K;J()$%`I9Z7_WB_4J$to{#dd{2I)P^ z1>G?Ix@#KrO2a(658314XD*BaO~f;)@ZsCrss=n4YjR-Sbr^1?U-vlUpN=MHvV_?q z+OTWwRZ)HU1jcJp9UKpAg&*o#X?lS}xMRN7cO<$N2R(VtrM5(2rklVRi|hm}b+?C< zZ>vC2rn>9TnR+qvTzFUTsXCk}#Xvg2VhsH!{`a#{HLi_VU*>V?$G@R>sQyHDqL%aF zuh)6=pyvGGMmu9CjCol)w>i#%=iL{7{A$Kv@?A}-S!Oq$$UTk{GeyXimvG3EuN=~& z$9F`SjDUvI?MM2R^-$GtTEv*A17a_iPAkPuq0`~kHuI2fJixdf_J)(}IgayWR}_#i zbG1>U;pH-94*HPm8bTzrv}^0p6dHGH_g zKot&;=a(M6A~{Hpt8=NbUA1u0>lboOjG_L{EmHJM12K+bE=@z*0BM^jyYsHeZ2s-HQCo=T_*64 zTkHpEnk=xZ=MdKt9>bEGZ{vTR_>SQM8p#F0q{qhD*0p2pGH^4M{t$tn0H?qg&5Vu1zq1k==id*z`wT}f1iJ~iGR22={-dax=kHh<2)X#m$^Vj-v&RYjE$NVTQ{6}77l;k(BoYMK-2MhEm9h_do z<2dM%xPEq=%ncNpXuH~wvDeCgQq_gG-xXE_{{P$-$uqdUt`T;!4PSeqhS0VaMVrPw ziq40Jbz|o1fP3)T`}0wSz&iTMBA2=kcg9Z$=H%AFOsAD~OjbFJzUJ6^)O`R(KBVQQ zCsB}JLTHP%U@rDVRS(`LdqFN&dFwHQE}TmgUe!5}4|NW!X8AYb;csRGXZXYm*rD!r zUDLG*mv5}b``jnryC4ArE}ax;zRR5_s9u9Y%z;Hu&c}gasLstdlXWQfY5XoOynPGa16~vDz-h5*&FBq#Ir;ajFT-d=cE!?9gBt{To=o^7&{8^b^M`!~C9d;pCJamGDdnV@rA z&V6)C0ouJ7W|?2?MA6RaoC6v|&?6KPpUgc1o=vx`9@-DX>W9QFu5Tv59!@C#E$9dP z>09B_9;0}BYrOeRtq$z>G%=?8*MQfDlnu1?vUe>^i#6Rlw$2x(3Yn6ds_Za^7 zUGI-u%QO}=Y!ekHdDH_xmBym~PGMGsJhw1;&rg!`I+tyniToqBJ_a9$!Q$}4QXj%S zAqmP^$J%u8YEiUw5hnYve;NHsEfe6kXd9)%H3(Uc-To<)oT%07LIosyV9t=DV@ssh z^}XExs*79`{#~<6V)yETFA?XTR7nkCMm7Kb)H}_%d*qL)!XV+0`No+#lKb3H8mHqW zl6OF7^$QnEMqn4^YH{|f2}u2t>>~4`56+FTdM(5Tz#hkYKu5S`Nf(@|cRnme)*bz9 z0r6#c-@Q%xg(2y?Z#q)>=T{M2<^3jj%YGU*21?tmN0R(#=nu7zq*rqPrOLK<;`MkW z=}X;jlDqUgyfJ)mqze)T-u&t{9w5(Y58<5x_3*BX-h4l8C&Z4svPv8r2VK1fp*_dk z;FhI8{OO+)5FEgg?RjAw947vH-BK#X!!h0MW*ME>SNcSZmF!uYgfnhV9P37{2e}Lv z$oud~&rL@{o;Bg@!OsV?zqg{pF7enK_r@UmQS=qR)^rF_O4{jX(+LYARw-WO+|V+v zbNB1@Zm?TDHac(FiF2dE8d{s2fL-6fnhCJ zuFP?;hKu-n>q}&Zm^OOHuqXta=?{3~NvR~!rd$nCwwFMT#O^#e!D*^}Uhd=hp zOycvOZ98t1x1u2LKAuY>6v(}$*CA2h(jeUa zY;w`@LLEv8#+*&i8X{iP_2oB&UzK3Fab(1y8MeQZt={V22)74lIhvQcfJTzvu0^^W zAww>mBwgOhaXr>?~hg7l{??6V9|}!UGhpMzIM~n9;>Ks2*n4)~5Fzp`iJ(I}%2a2fz9r_+GqR z50Bn73kIDd9w()_syz*@*vKHHMO~T&ZyayLX!LeKOp3_MD!~yL?vPQonA4bc*++K{iF0fhxQR zZ3}hSCTE&q>EMn%+|O@ zQVeJeOx;1t`(YHTYYo`DuuE~TQ*i8EtfC@LBH#Fh!wb4YC?go}^Yl?4TzPZSXWG9R zKi(Zm;pQoVq3=x7EmZ9gyX&{nkBuN)P|9QUKQ)L{CnLT$JPgOwOHE7NPX_Qw?_^Pq zM+I;{AGUI>Ap5$Xg(pvUX=BVLbwl>Ig*f(;gIP@^5A4m;NA^q8< zyK}eCV{n;j;%U`JU`uK<>J2A;&DpabZG(b=J=$=VJF*Owd--DpRQj;oltuT9^%#tq zc{e*!WnuOBw-*bUH-HkY)XH)-2PMibbeaba;-cW)2hSAuz|Wo^68Hleuo>%m_ck zM{>kkqcfd8^@C7n%lAF~b3aga2o1|`FM!+cliefThT+_Lh2ZnwRmfL4_e^J@8+LaV z*Gy>*Ky0Um-`Lhya5O+I%T+BF{#miqOZpQYtizOPR%QeGE7#4u5hJ;_cB^=&&+YK< zQ@r^?RTC_Hb2b0jUj>w6yO?WN+TjfB=50;sfDESjgJVbPL0si*QU6cUr&y18k@!0s zxfgFf9Vfh~;(DDczrM9&1&;?sF+CBD)MNw>7=>V~?b*rsCDJ!3O4U3-`a@=yGyMWH z@*&JZ+<=$#8}>+dhk4NU0oVTG^j*TQu-NQd_D2qqQ(1m~lijuu{G7c4pA!ysrf#oj zig_Jscf4N-p&x|%T&G-j5--5n&Q7mSr>4+fQt5i@-%fnzEcT*rYa3>>OZdws^y1`+ zNbeo*Z$N=~fS+4oH%Pzwv$;8`0Y|g`&fTW%0=`o&>%{}bKqfIZ0(%G_kSp1Xu8hnT zX`G_(iwrIWTRY*ruHxzcafN;*<$v;2Q zfpx9wWoyKfVQ;o-k@}eU(c8V7{oVS}@LyQ?1f>OjOei($w2+=R{q;=^O3j#f=qf(1&@J?g9#??r31D z-rf&yB#KzBv2^0ZyuKluO#{ABsh{h0ppP?AN1mbo3|S$DP6pkN%OlYTfI`xAh}P39-sOvMCY{J=}EE_D?^Y z%#pg?PB+LmB8DUK6!#N&MSWbad!KA@U578p>1dFteVmY2n=cU zck)%j$-+ab7c!d2`zqf>_g>=BjEughHq;HmgV9nOghP<_@+lwt9O+M<|Ihut{}}Lk z>Yu$rJY-Yui&;nH)xr811^t}$f*DvRP>J&7;=rQ0?9s&t%i-q{Q zc6@e+>7-l7AZjFx=xrnCcC&qdE=I7nsog1k~VQRM3 zgZGA@;H_zCG)pfi?XViqB>54>_fzT;fsMpF@8hocz6r!mUE38m)sB2$WYnA@ zI6fUH*;LVkl(5P3xqieaOvk@V=wb!*bgU>@ejUPz=-tutLS5Kx%bD)5VF35jC)s3Q zNWs)O8<&lRec-mu<^etF??m`_ejF7f+-{-oY!0+24EcDosU(*?2mGy?Qi~;cZ|D5$ z5W8%=!TVvA(olr{S4K=W>5_f0y}l#e;{t5k(t5uEE0*sxy z^Xtlk78F`;mU%zf3>Qc5{N-9|#!W4^F070ao?NZNJCA{SkQ4T+;{R0-5@Yj>@(vj= z%C7Z^cD53i<=u~E<}~36Zn=S;f5pgN`2DLp=@}dLKDw;mn}{cbl?oa9+MsledU?B2 zJH)(oKkK8Mjl}{Cn;)GZJlljT9yW}mC${d-w4zWi?rObvN;NzSSceAUcZk;mgLm=P zbq}k+b!%jcC_M$ZTtBunj0`}|;kDF&edN5II#cT2HUg}7#!%w8%ad;HrQgjDq^aBOu>p}MFG_XYiUoK#?o8Sia+p8jaV zpA?}@4?1X=e_-us=}rp5t?@uEH5wR&{|lKphcLEs=WXUkgaqejs_YB-=<%Pp_kOkS z7+xa0_}6d*J%bhO&eGjq8Vlz{|PQjxloFaQ^4Y8I=5p= zKgj{Gr^OAF<7C81j)Lnsz%Q!k$ePiI0zGbpa%8{zBy=gQJ)i_u+Jh7v#N6=1uWv#7 zWXYVew64Enkb(l@#*1ZFih8X1Qq+Q*{UK-eh&w1n+B_1b$ zvGx+VI)*?HtAjfOng2bVSFSVv-i*?ZrP6gO$@iX zkwuH_3yrTX-l~YL0*!=tDdD05EK&)!SevcESKk?aJa)LDI_~TIyihO?f9X=?6VFtCc%yK*fqZ9*l}^L-^LkojumR9p-=hkQ-%b z!$Y=Wn!9t0p!kk}EdwncAHE-ITj(Jkwl|#u$qmH+#OSpq_q!6?|HKS(_e9`2#>;XK zj*>jh2CL8XO*QzpY0`7Gne-Qr_;m9U??tw*07o6^8~o?lc{4|?8|Q+xo?FysBjJTh zYm7$2LuvW)9IaNka<@F)uC@=lG@0)TD0iWOChJqj{w92RSut+*LM_-wzmG5zOvAQq zhEJx#^6=PB^vA0Lo|H3V*ZqiF+u5+D3Z$}hcHy+zLMr)gQ z#_}6NWe@jS;GEBo{va-rTfZE;V=ku?&N0dV>Z{cu(- zqNNQ24`@8WeJwD=C6=FT)PR|yG_j40C3s5Vp})UjH%LtUo$>uI3EN6S+99<8Hgzih zH?LR)e`R{N{bm}1;yCfr3qs{2-}2``?y*vAFm?PTy+nbRTTM-+or#yoYi@stMj1lM z?C1%BHV_e-P!O)8KtWs0?y}AV*!QAM4LE;+sleSviPjOwGrU!1!B2sGzjusstJlFL zeeIyy+Im_eHI64;)VSaIJivaf|}K-8N#pXaL~Wl_`OyiJi8S!9IRD@|4Hho z_Iygi&Bx;J=?;`&>*4w`qo#Vi|LMo%$(9z}8qwnCTVIB(Z_AT5M7=}v=o=SU)bn7B zZo%yF>khbedvDJn`7YqQA>gLGRtq}sm}k97?&tWc0iVru?s#4*lbLnKA9Lq+`3et{ z{8|58$F<$V=*Yx#e8!E;;cnCA9f_pCHHU72>o*!vE_jF5YzGDHUL4X`|EK|b66M;& z^4nm&$LcQ5h@Uva$dGkDg5$CU(N98T+Q&**c6$syRJPjAO{Q z(rLuQRttmIC|U9IgOKk2_S``RDr5!RA5%-N!^bO4ih87vx;9`_^@z-2`2-p+1%9AG zrFX+{cvUstIw#t^|KTvIv71^4-I+w+(*jKwh+l4)y-N6;S0mO6m^pNioKRM}eIlb- z6YhvJVJ~vEL|0zA~}S79(_oLsVoMA*sTVkFwb*dph)K;Q02gXAjAp`~OSs zaX!%j_wN<%(afa~J~2bHn)ENIsk1MQBKPdMVpZ7zvgfG%efC(*?^f_ymrx={=8zh5 zrJ3MS2kUd`7iuK>;qIN&ZV3#rC?FZEI!UR59Vs)#p;k$F@6j`=sbVwS5 zi{fsqhkn9&V=>o~ZsHqXstD+=4uvDx<`Tj2xhU^fb~;n55tdarS%cn@eYomm!BwVe zWVC4b=T<=Y1;Ps-f>S#n^Wl*n5`#_15O6C>JD~}5npYF|bF|}CiF(o9QsJlyLIOQD zjgWG%G4exBK5oD4qnSqLI7bCi+;Tj9A;;L-C_u3WZl>}_m*_wT6O zmQg}_tq*ktzc!KOBH9`nifLWHAm+)m2gRJIB{@5a%<>~dM2HdCdsCf(Ff+mMP zIALm0126vkI^cgU1L@dVULDjggF8l%g(<{O`^(DfYWa~|F!es`b96MD^ahNJ-XydF zxTt>;1mczA(oR%$Ys2ptO&7S*LHeiyd@=zgSR-8aI8!VSJpOyKhjNUDCI@U}bxDr9 z;?us4^ zGSboZU0uk!nQmALd(Z!2rV$mpboMGM6Rv%Z-B8ZfMs$(pZ1g$aj8grv&!*e5VZql& zVE9rwz7bfRq6&yBs;2MvL0awP>-zWv`kgo^jr|aR({j_UwWM6UiMNC4B0C}IA z-*!>@S&vDZ_SpR*_mZpBAJaelhzym$&}Hp_Op<4NxxVLl=~6B6B6}_?d^ZG>ot#<} zdNMx|?1-$It;aG8^Nzk>eVE2-bb$LB=?R#QXY09AL3={C>CuxuP#o7Ps*fna7xtT5 z;>kHJNRp-C;Pnnnvc10?&6JJX>kanJiI80FRDLS!6$ybH+WVgLmcsat&CHgUK!n;p)e?&` z;)jd8_e;1P-_6fa*U~+)IB3VwKfk-7hvQ6TU~M_RtlPTePk0;(`l@C^yQ0xoUc#PJ zg#!1-#=2!>OR&ISm^nzY9`)sP#RTdaQ0u{bXeHs6&GsKT%eb{3bfm8C2zr?W0nHcM zcirzn;e9SC9L2pDuHK=1VQT?O{iz;vzS~6dhe5e(_QVTrAoI!VO(wptAF_!lDFS-` z+Va!Sn{djGWAjKvKa>uftIQ5BL3J-`Z=WOaR{dS)8h@q(gLbdi?eu7enUddTzr&K@ z;emx;hjcn1DtyVdZvAuIFJWCv%pTAude}ZAtOAZP@m6j>)PS7~c|D;Kl^}kNW@av0 zk3Gq!4399jW9R)s5f+6C(0+ERv?{s|IQQ?p60T8;w^h|VZAcI19ZzP%{H9)Xdm7HT zWw;*sQyOje^%vlM!*^#`1G})HI?1T+Gh)rH^9A47>+z@k`J5T`E|AP_mU*7v2RC-q zyQ>Yhpt-S$ZpiFUFgV71grD?3YsMkuwOALP$f)i0j%r6<;WG7PhBA0dQJTM6JAk** znVa*ABQjUm3@ptyp_R!ut#u^Nl|G~y_RNm(v{-KCvWk^K&&QMNU;gPs&(_9h-woX) zpJ>x;JVe3I+^pdTN#3VzpIp1(g9@;J`KM+6Z8zjRy{(*-)&;-T?2S)$CqbV9(_*b@ z6ZSa>$lXk6uX61u$8lFGbVW`%SZe^$I1ubz@vw>@%Ao|qTljQH?0cq4wwe) zO)7;wGWye*%GJQ6RymbGc-(;!0$D zS3lW{3&6`yQOqc|9Z8K-Ks_uA6IO59TCC-v`(foFy0;jdA6|Bljd$iX#2gpr1H zzQHRm-`C;1aNJay92K0ldt4nkL-JaglWFJMf5G;hTeBt2YjDBi^uxx&UNBpp;WHL( z08SCtWnB znJI9Q!C!FQ>mn$5<#5_5p8(#em>q& z!}Mx<-e)N)3`us3D+>%lq(N%bx7UH77c_NaM|T@Eo!>MzvT+2ovYPUP?sY)J!HHMH zy;MA6aWn7C$2O9~i8YBA3&k*QMV8bO((kHuR(k8*0RCD$%twX$AXwx6Xqqk+^s7!M z)pu3lu4>tV(RF#C+O;~aDAoc#maUui@;5-Yzz!L%(IVWkX{CqhaSw`Q$KMYp99=t} zTEPP82E6nSneL6o!0XKJ8-ekJ&mexT?$KG26KT-sNcz-*bC>LUOxa68U6VO}fV%^> z6!9_~o~2^B$SdW)xm8f6nRqR2S10zTx2*rdm4fe0ZAHz*2`Bh%Pjy&H1MI$%RQ6pX z8zQ?NhV2)NM$>Ke3}-u?VQteJw#P{&@QTZ{F54><|7qn)fiKN9XxZ~iI6o>KzlCk=iycdWf=Y{j#|ckbvP&Z7a9%HZ7`Q~+)W;*I zjG#iqx>hLJJZ8*(u^SaF#)Fw12hpdqn?5HQ${D#8rm_A^mm(8uGm}$o4N}*hU2Gj(#Dw6;T)M~m(SmSutfapmx?Z>_Kkp9^p~a_@maLE~c8(k=nQOrhKh}nmy*f!xCV$JL*+MkA<4u3y@GSltWDnZEg#xDW zM-1;eM4^^!{YWEUI=Znp1ik1aIXHO;s&XH}`Tt(s4_e4Y`^5O3z@Is2aWm;})rS^5 zRqg$_wX+P4)8BO%+DXFzL%QsQb{RI*d{f;g(Tu zLt`$_htqchf$n60AhXFZzW1n&(|OkqN_kat*EWt|8l_~*b-4nZuy(n|dLkE(JC&&g zeX9k9r;|rs7>&bkU#pRC_9e(+|Mq3rk6D~|p1*p3nTqDRAFQ3r>)}!$lc$+R2%Kts zU70jIgyq858Hb|M&^PAct6PzThg7n3qDL|hBSuc@^B6S2;?D5+*$bBXT>G0uypN-aE$g zaQd`M^Jc=?Il<3%VExl#r1&j~pEsUHO5e;H{kdY?q8#*SzOD(WHiHY2*0p$V`CG`d zgVT8LdaPv;>9cRjss2yRC>uHgpySlx&uDw@T~fs9aXilT+;bJ_WjL{=SOiP0Ci8^>PCp(8d)Ya#<}=qrn2C1Rr~?_PHGMMDIq` zzM=rMHd?v5($JNgUX$5+1TWNYnBdcFfT%1C%bcU&Q|sC_y{nCoaHTLYctr2weQqi2nZ#nx<^{fcRN~FNt&{(Re8hE+%?fu9!V#T; zSCVyO*u?H~yz=@izCJde)fzYhQ%8kWlCF+o@D5MMIF*kulDschL4xGL`-HZTo<3?y z@XD`KPQ(daYC5d%LudACmHZEt(57#Cke!8s)qi*n)D+BtE(3FWB*_s^dwx9W++Bq1 zoE?7Q-Oc##OLL6#(J3rCaQc>281Yo{Uo5gc*MwhOd0Tj#Ch$KN_Qf*Ni&_nM_>F^o z4EMDwsP#(^py{-A!A*|p|`541-(qkxL8on}K z2|4(>n()yrVoSp*VADxkX$p7+H&`07Sl;)cw8`H6J4hbM?@+1f8u|AwZF`MFOU3Z| zjV$&C+uG9R2CAsbDK=n8&f}6b|Cjo_B9EJ`92h-~A~k?R>oSva*#bFpWFz-=CNp z97N9pf*PB=`|zn0z4wFsXuNn-dD5HY_y~$YdXn55FGN)~2}m!Z97sj7t1W{0S(Bc- z2{fc!Eo~UgoPoOvv6ifIWx$wuAewsR9lA^y|Kav%#ZG5yGY0)4;7pR({x7r{UE7{) z64c2^@q>SOqWA#7BWM=V& zr2eFDt{;v}(;e5!A3+n9^W9pXhH?K6JL=%+c#_lkv;kxqAb;W12HO5AjQ{m8IlaCa zPdq+RH|f_*avgs+3#PAhD;Nx6}@uu4;cmnCHuVGF$xH(Pc2AY zX$OTvZ;D34;!ysN{intI-6*WUgFU1}Z z^)sj8VIlE%`r--rzG-NxL30?_TP&SSV(WoFNBKPtnatrvv*IV+{*{CjYxA4;3-Pbc z8qem2c92}QU`d`i6-|2r?)gOw!ugaTb;|28l(~HS-Ls-zEL*w8dnus>?l@WXT1hOx zpI>azkK9UdP?1IUs=YV(eX7tfe3b*jyqPZ@C#yk@<eZ_!;Q6p) zrt#$YtrfgSU8(K^*-dTp;FTvCLe5+$it$hYQw@mF(6<9GCYuuE3Tultj_b;*u$xBVYv)%u2KFl%6FnPH+zlj zjt=}3nwVola+9}TKH;__-nSBd(~ee`VHoPzxjjRpp4{hs4If%{^@V%5-%sAMRd)}&iw;G=y*5kw+_SS5A8iX0RzbFe0Ilaw_Nb)NPZfjPCO+} zUAnpK-T1bqzvX3q2k<#>yrZMnkNgo-c@2Rasi_;LiXD-Sp5FEnX z0iWo8rgS)K5~<4diJae9W41{73}M$1p=e3lHrT0@DRa@X27jf?f84yK8OI`cRZfw- z+V}#MLGkP`GN`@(ak*g}?I?<;ev>>z`kBlK<*sR5_%+(T{@^J7{2HnLZ=43R9scI) z|5~8oPzEF0HX4K<4hp+{k=z5h*WI2?X+k=!3&-hy{6b#&s)XrtL-_3tGb_(vJ4Vob zm(^8|M;<-t>?yw-G<-Hqn>{*?+xCbyyh;v6*(WvGipW0lKAg2@(-cykmJV!Z=)&fet%G$aXtgq3PiT%G|i&Ayp09Lxe^C*nz!COPdq=z&MqWBX(0FJ z7~3~*VqoK&|BT&pVqve${il)#r-5~I=Tn6+DsHGR%T9}=;U|bJ;L~kJ2ZOeUvXTSn z*&V7QyEPS7*7~G+5B300T)g&n!d>za7GyK(D#8jIJ?B`#VdQb`V5K8`lpR3!^m>;RJBOEa7u7j-cfyI{p9>W&4Op!>Fi^O&8TqW6EYq4vPsc=1 zZ-zXFlalYdVst3*ucUi{FKY}1`HgxvZS}!^(cDXAmBg3ST=a1>$v@XVz0;$l7>m9P zNyn5Fo3Y!}CDrTMG}fji8hCe3V%X~mMf>%=a96;@frC^=YEY}| zS3T8%NIS7r_n`AE=79Nd3llh6Obb_?==`V zfg{!1|BKU2g&TF3MiOq-;izNn?FZz1Q>1Y{=}CVL-utwUN5;4qRXk5_(Yo0OVvOpq zj1BrB&$c};sHz`!TKjFeCHo6_S1N*QgXd76o^re|dkPa@Rmmn*Ea4V$%et@L#0z>_ z-Ti380CIHGDb9I~!?}dRvm4#gAabu*%V*+xo(_!(e@r`&rk&d?2yaywaH{Ey?xPX&%nNceT`k^x<@w@Tq zZshb5(a~)iM2q2yPcOG7p^%V*P|OGo1Y|gJ*<^;$Lxw#sV~&PR>Bz9}rvtQZa|jkE zxnxtpJ4S18yi6hajn_KjiVdXasH7f8dADs8R3F?sHzV8tbB7N; zqej(0!rkUqhd2fi)Shwq9WOu~eWwbGWa0sx&>c*COg!A-?@!4s6K<%D_!BzYT8P=H z;C$_KGaAV*-(St2gUsBbX+hpB@YZj6eEucbpOmJ~jq4L`*f-r4*jSBcyXM}clFz-T z;IoaFE8#~!GnFx^iG;>URz1(i1Po@7@@u$5&ZV~Y%bgajXraITew=9s{(dfG)_kfB zIpn(^XdLQ+d(RtuhS-R;F!#K9`|o&=6)JT6d3gf&sV)0ft{S6~8@m0OR6~{HW#OXvDMgnIVPs%{sLER~_ti46e(+--=Az_8uuA`yj!^ z6K&EeS$OpDeBbIc*@v3heVBbQ2WuFG|rk2~0 zX_aBvRHrnX;FJpgbkEs#$XiabO%pyd-$LoF_2unXR&$4;8zp^{HJoH284- z+r{^cC*b$dm9?u4(@0-XwfnAB5zi_WD1yqdQ~OpnY3 z9b@7@znOf7m8uWf4sbVNfLI9Q@q$*gOibvn`P+wI*A2SFOHW7kXdUQET#aP^?QCj_J9Ay_@ zpB8*Qh#?JOhn^De-rnuJ>yvK{;+ad0Ub`5E!M^pE+jgc{e5hYEthOZ(X%43Di<~sL z-rf5(_~s#%mUD{Ea2QN2pE3G)4#KGk)?2)X$@%Bbe#dVL z=_s=_RVhIFA4*<(ET%7JL9wqDNm`>#K$0q#q!6;@bREfpfB4fJSg~H z%yO**^K3M$jGe{o zArmnvp+;QNSJf&~RH>+77N6 z$W`@LTAxjFUr}!x?7dUK)M4Ki_Z{hAm9y@tdCdg6YwZhe`$om|X_ZUUDT8?O+Imix zaBs+6e_x$9Zw}WU_t5j7`i6N(kvPyfh{fkW9^F*sjonudm~uWSg6!oR$6nCqK^TLB zKQ0!+_8V`(fTaMVx(l_AoUO(DRbiGtY!tj|$F`43cwj;8Y|fue%3v|rNLA9l7xyIg zzPxd_6X&}tBC}V@&{l`9r?DU%xV7!Y93K(ta@&&|Swo57_PE`P%We)2z)da%{d7!y z?xmzca!Mr@uWpDR_zp7j+xEAl&!JxA^lV*j8`}MMTJ_MoHt5}-9$~0V!MtFh$dq4H zH2&5bW1l;S*XoQD(%iD~j-t<4ZRl5UIQ`}8%%e^yN@YljBfToRy;8nkTP~Ay#D3v zv38kcbU6Oxd6RlMn*I7G?Im9Wq7mJZ+DC?vaofPL!ea|q^HDFHeXa*iSDrNda<>r= zC0G^teP~88KC2gr4@rN{v?p}+#{@_o|9(G1bO4Wvdn^vRk-Q4Ox(v^TY&@vu_%@F!Ytb{Q!PH>v5ZT zLn5>VS|pw0)Pmu$|4exZNBv@_nniD}F-C@qH$h(=p;qif=_}0?z zK-0ocq#G!PpNi)%IP|YQ9YSrlhlWyjVgDKu8l!~OqRmp`c*uC zoV~EqY62M_RtK(k8pOOmtx@+1htTZ%AhWv;$z7a%u+!u^6)Z}hy*nM&hQt6D>Nq=w zGuhFXl;s++T(W`gqWl~R&mY(i{#0O(o@XZ?j!nP&y;Tc+yq>I>^swp1#JZnSxG=P-E>zULHLub2XjuVublGcMt( zLxIXW{aECGsK4EVyjNyLo{KkhFWdeR8wsxtdW|-<4t#*NF;~8&lZcwn3`TW%Vo(!a45KY}%VMjfu?K zo^HRVu`*7jPh)>M@b%Wdok$yi%iZ$H4+ZL>>!$dtdCg*Iz0Ds}$Tx^}Ya$s`={9KD z%Vq7YQ;&CE9W$WI?t|TI4}5(Vd+_Gr<^7TYUBIRB$9(&*D%9>?&-a;S0`4yF;&D1y zi}BqqbRzRK-0sz9jx8mA(bc^w-=9xn>h=(USdIa5PGa!ptxm#OrTCzY zQSad8z}xy4r>Ag#w|J)UnNJwYdqU2Gmx4O0$Ae}P3!SK0EtOi_K1?{Mtd#>*+hueRFDm_qV;v`V@kBp24iGbGL=KaI!N z#oKf5YQblH73aH$Y3R`Vd6;i%0Jt)b`_tq)VW*jd+4v3OCB5bp=O;UaRfUgTY5RKM zl*i>-XJUPJmBF$vv?4&nu%s^z!3gJ}c}E%v@rUEhzvbAd9K1Zp=uoardp`{%utS zA0>|Z%GK7v`;B&D9$}UE*Ey!7P_`P+1P&I3NL1k75c=HwP3^e*)t)I4-8ziS%Srf6 zs!Sha=^PCj2H{gsBa_HB5*`rRAUXK70pe?!_kS^N$D|;ma376PJakj}%LVTUY!)(7 z$jX|)0F&)DNrdOSVeikcX}{Ycv}jl4yJdOVn3}4Mj$MObnwY{2lx-JftG=b8FB zM>4NGyjA$2Ybk~;?^e&&Z-r&}-TBov1g34Qw+rr0!)qJ(DGK4ypkTC_`fe^7?n-CJ ze6nf-)tiz`>-DB^G|5CZlC2c?aWQiV>NKOVlTC~O>Cs=l7Uo&Aw;Q$;{{2+*oa|rZ z#lG9q2(R%?@2m0AXPIhLmXnQxt?rh-Li7t!G6Zw;QrPu0NT)wJQd3I(GER8Y3W|nmG(HwQxUFJt zfcQ&;H~2**fVVqe&EfoEJjWmu;T+k9op&~M$Xw_}|GKUaHrek0mtOAWnHfV{PPq`l zaN<+2YUR2RKY`oj*Qc>6EaT$NL&0E^jDl^nw+A(P@QhSxThq=(oRu@!x2taq$3FW{ z3@=84Qlj-e_LFV6VfOgOLls$=V>KJ26`h86%yRkTaSFGIHgIk9A%2)=){6%ZJA*xw zil-uF6#Lm$cSee~0ZqWSP`-Ns15K<>CfiKmTXyQUD&S_Z$#{6tA(_evQ#&n=|v)Bey5Uxg&Hm33NRQffbUR!u+V)Fqo*y7c37!CcR$ zQPbcs>37tmE*Ln4-%SdIRiJz5G+U>PFVIgU%FS)0;@1J`?0~1UDA(}M`f42MB{S81 z;8tq_^L*=28Lc+(FYXwAZCQq~8q=J;mt#>wwsB)4D+PO>W!VKf=fi^@SpW8AGpL47 zC!gL$=68B#GP)Gf>o^lyROir-O&6Y>sZwabquU=0c1t#ay}5y-&iD|t`FR|>7IOu& zL^5kFL*Cvsn|1({y5?q{1z2j0{h-xHMwagOy_?9LaXD5mgy zPX2B&gvs~o+%BHN#u)KN83n>6JAHW6l06g*+*TL=a&;h69ep ziwib+WuaF!$Jde_BUrVnEXyCB0zaJ28F$;~f#c_nFCr1d3;o>8*LkoAkEO?Q6kHhw z8&9{7=gB_NE^NSV^Yd=3>K8YBB|ZYKDu&nS1$u#Qxsf@f86nUIG8iR`KvMg1+J)Vp z@!O7TWzJ8?IlKOhL10iDI*ZQS;1R9EZK~Dk(SI7?@`jhqO9}l*Rr(O5ylx5$myb>4 z5>8fo=tyk9UkbR+WOUS-4M3Z`Q_jug&-jPRW|`_UkAA-D!zJ2rcv)?*iM`+hSgs0) z#TyhOPin#~{>gsqrK<028Kz)t0BheHx>0cPFk-RUG>6lPKbaah8}R4Oo#Nk9OOdVE zs;Z!ecx-PkYQ6F-!9%8^Grb2&A?(=&UWp%>I3S^FdW~xZZv>;9)F}!!%oc}66q7mO zcw7F;he9Z08y7mM^Bu2HK2onQ%);%b(&h)9=CDiMEWC18GZZ`J&Wi8uf_0&lPs6Js zG2!m}^Ips`@b{UZw{=7+;rOw9-swPigjwcqHdRvKGHrX#SS0aUUF+i!NTcGtkrhpm zVZ;-EZ z#@G5^3_yGg<(tRH{BDe6TC(G!p`Ig%5ANKx^RE+Zvo@*7VV=T`4SSsS(zOFGr6RwA zoJ+#Qha`D!k$ls7g%0V?K5+8veiC$k5Sf?7EIQuNkR?Fl${GK5xR?LS@yARuvd5_k zNb6EDP7C!uc(kK?WSM+tO&iv4?Rafj?hiX}%V?y9Powm}n@v1q?$J3Oe|Wo0K4$xe zdu@v8gSh9J{nu9qG2wkAM*vqjeqU3GpUWr!hNbshIqQjM<}!+jFHPbln*_V#Kicu` z@tgX~pZmdT;pW?Q9W(fEH>+pIsd}8?aa&UzDu6o|;bh_$8mJd*e))Hj^bz^LHvd{j z!GOUhGN{qm;5nQfK8)Xej4 z&xvn%llMqG2bs^_w2d{?OD>0VdxfIEUmr)8fEM9$mP*`m&6RaibQ2_fqo!_o+>ZqZ z#U4gvmO;6=j=EA^1;jU5D|$=T!R$JJj|;YQ==6g*@jvq3Hs`1oi4>@5#dlbYnzvOyIfZdjmpKoJkDwI^s8=YuKjPh zN1+BzP5-8Utv7^fih)+21bdLyPS`?>0`gS-Cq;^rXFLK z7ROz=XsED3mT%ZMANFu_IJ~VG0q5@`YT+#{kQ@0l=VoCibce68glA zXZIkYII|4z{S>`?<31HkBWGzxTd2r%Oj}EmqX%~RNxoH!9ffZm3wJgIMPb`k<^pD3 z8Xo!j_a?7MqD@M{7Bz7QbK9SgH>fX003Wv))3^?OAzb{d6GV=#~i7WXrxK z>Q{x=Y&RZN`&R+)49XdOspas-&n{_oXFm*NTWD)D1f!Mpj(hW_G*qgW{1^DF0Wa^# z9;m3FMEVaBL)(AGLh+^>_p$st>R-C>r}xYdT#SDV+XN#ZU6{95XVnGdt`x|s?QOx4 zbj^TNjwxh+(j)bbc-_1z?P4VCdr&B}zlhJE0#p7doGU9CMY9c)yo!Pp9FE#}{iJLq ztjI*+wX*M^{jYZ0Y#j|c?~HtOD|>^MH=X6!W(V-?*V{ct$LFv&;oJ0|gmH{@mX7>1 zk%y<( z^W;nLxtP-X%>r5Ye!mpA-}hnkcaF9__oo6^wk;Uj`c9y<0P|C)Y8swpKe1WOpaioMu4Ym`JUBJV_Z*O#*A@}{Oy3YHP>Y!@&WJjsx2m}ZEo(ep- zK=SHS3Y(-qgWb4jQ^to>yd=o)9Q2fm>KfG=oKF_Od-v(lpMF^w?(1MBHc9MrqB^-S(!UsM2OER}05>D{#uREhs3wl=u zr}o;C`!^B=#NFPvY1()08zUrA0z=$eoJ zE$0!`qtb_WklvD$+!3=x=4rgk?)`bPVGbFCqf>+;30lM7q%nZM5luL5i-bO@$77R6 zhU(o&uF-P4mtQOe|K4=J5=QO;LvlZvwl3D8%z?+61f2~Wzp~T(2lq0)NvEe$UG zQ%;^>9)&X_pG%|uYlhbxkN-*Y&%;5@IRWALNgUlX`B+i79wcYy^t?{ak@IVru=U3} zme6az)_&ayaiea$ zzl|Hw=ia)r-&hfJ`}vA>id(UqW@Po~W(P`qI(9KhdjRL({59S$-UVJ~YT^@=sSv1S zy6edp;pVJs$ZR8dy7gy89&KJn^2h1yU#j;1pGTJAw|;RD$7_#@eitc$S-XWEuea@} zR4#I2L7 z@0kX0Ie+!;i;*FWaXftW%cD^+EpNa$azAu2+p8DH8VtdsM}nOM=g_?HWF7P6Cgd#k zQj|=fU;zJ*n9Ujk0BeWheY@*`J-m$@j0Rwb9;wv|lm51GjTFs08ePneG=1JpdeHvc z58kz%#FL%++zMN}!Ku(Q1%sQgEL|W)@z69D@CtlQ6{SL>wn=`sAPu*lG}4Os&k48^ zmUk{+7{>Z1FV=e=_yKWO(#=jBoWK?OO(T;Nb-0UiMwqR87J>@TeeB?##Yd4pOsjT> zfyL*`K{aaMf%n^tEO*R2R?mpBXtfjX_p@lWKGs^?MIT^bak39`65h}|UKmG~{cQ_8 zgg0Stb;SE(&^stNDyaHeX9*XqQvU7oAal)$<+0)BY6ySz$l&sS6x{Xb*WNfoa*i@) z{9!&j1I>>QvmMz!jO(&1L>?A)z{vw|=^qV7fhqfY*Z#Zhu&?U4ucB%R-cieG_!Kt; zKcBw&Dy}?%o4h;=!l+YNPA{!;g!UaCzWnelPb3+tM85ZZo=(N1lV%EYW350}#s6pN z?Q?8v6R>!3cocUH=Y_1!kk9)m!sq#|E?ikR`ORsh3qR|gGry1#gmAJr>ZpG=F6gY& z3$7jqm+JB$cV;9e5 z!)*MZuae%suLHjr`{+-*xI*$A`$19DACS(k<2UxW1}6+m&j|?iK*jS7U6pc0_;Ad} zznpyUPLv+{X6#&u>t(Or8g=+zL+9a6>m z_FCqPz_)62)5xFx_~TJ{a%L=u3G1s8(qe{ke6c_0o}%1%Ean@^@{(Bm2N8Aspp%s1 z1oEUWawM+AqwB6S*7_a8X!50QGBf~aZM#iDmkTxU&>p>PGya}X;CODpSPWZvW5#;tyslmwzz&5b_!LvsTsN~w0 zR4SY^wKc=X;@jVedefI@OkG={PsoPZSZp4?&MDB(c#NSq*N6*kX_%iOreXRS^VBKf zME-X25_p43xUzCHGGFlc#q?|*)F(A(tSOo|S4M^=llia(9wFF}En? z4|A6H@e=ecd+e|V*7eFMeg4&eb8W=kbWdqK=meILu=9=?y^wWVXUAr68cte`aT`u{ zz`q$I{?IGKm=B`(Fr$ANy?w@bKWj z(v)8Cs&&UUzCP_z6vD5m9H)|e@j+j_>=^7NNzpyJwuy&>p>Vg%OU&2{7xf! zHP$_Sqk9qw8wuZ8WN1e#7cGn(#+$%R%lhuAi^GVzaGZwvMKw?>P)zzV%lYf5Z z7WJUpPSOlZI8Q1^NA2$W_Ewa01#WN#*PsfgeQvvG79fFD?z`S35f0NH@pz8853^_Q z^xz_QCI`wc@KEX&zns!=1rGu$fwoUBDJ-%p61s&9e6)>P+3VsP%r zDFemacAR&ra_O*S#x&4KM>Ke9m!Qb~wK4RE=TZG=Yp)^N< z^|}wjTWn2|>sR1MjM5hT9Rj^(XSTPp*iQ&PJ{K1VuxQNEUsN;+o{@uw$2A*a&gutE ztwIl?NosX}w37z>TACX(qVrHE6mqpTdJJ~kUjE6KIg04BS+ru<7NLB0?HMDUdzBp> z$x};6Kq991gCE#$frOc)=)=`Wpr!Anvj07b*cD1@OYrwNtovn6pV$vKNIxa+&XT~6 z`o*VnS4$Ber;SS0_c8SCX#~A8{{#|vTP$MHPy-680+E9-2nLqTUZ%4n5S9yq<=$ij zNw-?xbax}t{#=Xp>)B|nt~-kLXdgWO6uGN8ei=MAIS*f*s6Y#31G*%&Irvaq^&{D< z1=UZz3%J)m4M&)7QGLX^kM@liL!YQhWPW+OlF<~CrZ6g;VFls&eg5h-W{4q zKfDI-OK*0kuDT%Uc2(LXYYN;w8D2-$YzO3QT*{35mCoybi_S?T!RJ^N`e4&-xKm$h zeeL`hXkUtpc&bi-eNFXZoncsq=9Ye@fA=UnJ;_ijwLn3Hw80_9SL1kI{`cv^xiO%W ziJ4YG|%23r_2GjfeTPpl^#GFLkEj`?t2APxV+YI(x4~&Uj}A z@vcwD%L`Y6@_Jdaci1RQo~r40I@}6zMl9+F@x1o8jw0VNtn1v`$1hj?xe(e7=V(W; ze&VIww2Ix4M&vj&%_^4O2A>)@sIG8~A#&V^dfLH3L^$FiGNo1w6|t#OQ~J{o74pj| zT)Y#^C@kZPbK}r!qbB>Vtq;ukbwT%ECq(33*jS(tK+&GiL{6we^2WWadc3X3>TYWP zqDUS3Gs4pt<=p{C=4t<1#(M^yJ7o_XZ!SQOw)VjLW&)U>@SS2|oknMFZK-SHeSPlt z&?z2k60r3!B-?W}KsmeI-<|u#5Pf)WSbps=`jDN#6>_Np#LqSC{MHyln?I+yKt8xxYaLIn9Nt*%?T?oda=Et}I7iw&A@(Y5hkq5k1Gdcq2gOd z0?O}K!GH^xdIhwCdDw_u8_p@^@H#fq{5TWMbk~`aasHFw)6JxOsYPHj9bKU@B7<%3 zz2J_xn<6n*-wIfectjao{RphTd{B6CL`OY+G*^T&B(KC<%YUy z6S{smActZ@f}=FYx}wu3f#>XsL)!Q>Wam7H72(c@uU+L#KKB=(ysxB#_Id*n_o$zW zm8k*NYo0oR52p~@0TPGI!YC+PNeT*EWI^xC?>P$Ljlg&YURT6+Aj3xt=1~{N(5cX^ z;eO`{Fo#7EL`4B7^@)#7^3%{n)xR0ezNTluzQXv^+!}KQmTHWFptmW@Ke$Hq3kV4aOk0nb7>be47 zsB-^O-o&E{>ZYNb{sq{s_F6O`mBHhXcWcb#c4*-DZhrT{70m~U1V6lkef$?K$_8Uf zKn?F7)58LxJO1p{YH|s>DQVDjj*kS#R-6pyPF2EdkJQ}kIqZAi?_qhCAqLISxS*bJcCRfIgUtBYJlH&i6!Y>8?x$6IX0zIfvEqh%NJ&DK|GAR zSX?5P;bzs5J-bmX_BnlFB6PQ)RhQJ3os%PI$NPBpb*mc4KE5T&jky+m=bgv)x|E?c zmB}k~2S<^#`mn@6h>uGaryy8{8|O4TuGbyuCxGb%d#93?N%Y4ni&5)&2?Q)WXS%44_nkUZ zL1s9w#6I~@->v^BXy-#S-}!0m1G%Nx@T6%Ndid)(CniQg+TL_2NN)&PE56#A{+5Ld zU+0r6{0TrLNA$kM^O*H_DYG{`+EAVvmA9SKJVY&h-E;d`0Se%|PV+af1z2yiI;t(? zfcJF~H>><1#F)UvWJ5m!&4CwXlcz&~YxCx-h{|%b`pwyU&jAYjAssCYvY$q`Z(K1+ zG{N(X%PB^CdNDU{PmYlbc^Xuha)O4M$KdmI*@$l6LG-s>w~YJQ2F zxP3LatPM&=%J&#f_M*SdTSI&Be)_WZ5v3-F3AmuJ*UojT6RCLpH*JRZV@?0*bleCX zhTi%#o)d2{H>n~1V|#%Ecsg1zb#71N+)M25mH z^PkA{Q@5!D)=?S1>3qyJlY|V6LRVLkNl>QsrP8~-30;-Q{*UXyJm!xN6y5vL3m%%Y znbmH6=uv>$R2<_ps_dI6jYQp0{;Du};a(-)qu2G^o9@E=Vd4?b>qF3SLH)|=FAD72 zrMBmx0oFG+g{UT;s6ewGuAj<(jloNkefK5paXtIr>^%X6Wys<#6J7qd0C&wNYlv@} zPzZ@GFTuieh zn3wm+$WBqkatbY3B$U-XUW9(@8Qm*}Q{Z?=Q8yjWX%B0YKQ;^HqaRK+O=mSK(NsHg zUL$`g^5NRMeQ&J=l}r7n*{Il#gpAiBpF|N5tAEClKi+zjmFt1DzT)NS#~pAx?ygUr+9yaX4g9Uc z7>l|}jE=O;6ri;9g0j80XJKcfiBZk9166MPiO&BNh#CYrInpkTf}k|PyUMi)*6G#H zp7l?J6D4!zuCb%Akn4Qnj(P`T^fC}6dE9&Jlg@^=xBb6_Wk^->7d+511vtJJ>y3 z0j3{K{uGb4fZn}~ahsC_P`iM_h6Khzri;hp7k<8vw3B>dvoNnR=X72M>nQL_FEZ{^ zsfHzG@n3%&NU%Cgy?*v$FP!jrbFdZr3j*fEx|8O*;lWt$rfM_x#RU5vuutwluh{#3 zJ9uNgQraC$x3nTqDVV*yBs-2I58jwwFieI*hbuZdJ7cKDg_1a#--i-IYOXCc)Wgl~ zLMLsUf5GgWUdAUq0#_`OB_D*2zz@B(_a+q!=$fR#ER6@&e+R|)NZ4W?Q!cs5a)OA| z$_#Wk5?atRw{*QCke|L2S1 zGvj>!Dor3-(uKgU;(chJyn|Xj-g{969uRs|H&K#(YUX@(dz-NmL)}}+;M1H+KdV$ zHA9`vUGW=nGqBdoU|2IWg1A?5R`;fjq0>$gC#Nk-L4ntjF7`(UD3+QIX&)*_o#(H2 zEjD){)0c@B9=9Su{6|Wpt;!f`m9E=-mrnwx`{^5~5wBum?#)A-TF<{fBDNr! z$wYq<`|Ub2*dwoJ=0Tij#g(e<5ztRd5`&9XsB1lHPYPWo7>o4fmPOZrj%&fWBfdB{ zSj6Pn8KEI0Lb~^PvAY-KyR#J;tILtT!kJv3NFo}|CDY`}l_1d=!JH!({y^Ye1Dyel`k0rNyFUYk81w6RdEWjOSlv!!LPQYk*?)Df{x9->}X=d6?1{3fUmA zHuV>C%FaGKUv$z3M4r)aZ(hhnT1>LcBiP^Vbj6nK;c7mZ)2_3B{6rjAj^!_MKJNgj2(X%!<3g$B|=cXgc z$odrPI0O4Qd>5MCRAz1k=Jg}8qV6N8+j5a<>?j3E$*cG_7ngyv{AlHO+>A%}ndeq^ z&w}Kr=TPpxPLPq)Aa%3VPRkv{aATyAxc5r2-gAGb+*B#$>~xi4H|aJaEoBMdCMDIH4tN0D%#+kd~w4LIMoRZ|xwqBrGkxT9`IjedXEQ+mcA zc(||XN9`zhrZTv-pBsmdK4E*FlSruF*u3xcWE*PP`aSeor5(*ON}17~^aXFWtO{Yl z02KJTDBq{14E-Q%Z%yeop^k?iFQ)BuBG>3UjDul4aKV^z#Xh_bnb34Mr+mQtzS1SO zUc+W|GVQ_>6*}y@h}_|DWXOW_mlCRnqSKHTSnn!n!gF>D?(C3>K9u<}-SEH3aU`^F4z%e%@w!yp!CbYDBD3I*x}_)f2n zp@t@gvwybd!2S^Tbmr|!P%pCUN|Gb zW|@ryF0-$H(nmGH*}4rk8=P}nd2d%xa(Duyb<49pS1AUXr=G(<@q8nVz*HTYUkzKC zQc-B91GU&B_!aXmK+dGyWbOBQbU*ad82K#@;&u*yR8}d- zd0gC4!ra8#1$%TW2J5cV(=F}%DxvW6ppPZa6S&Z39CNN~9Ej(p(XCE0(s_6_VMDYZ z-tVaixYaTXFB+@c_|gY}TTS8+d+HcE7x(nk_h&el^!DpyH&+VmWL>s?aVr3F*MCb3 z&5ohhE@I{U&wk>4+tKOB6d&_MsPejOetHR0Kb*x zqkh_DNG@wu;t)Od({EErQ++CfiO*CVb&?gZCkN*X>eoT`u`==C@Db$lZ7ue-O)1Ks zON}&;$MYR&`RQYxqhnVO`3vBzo90Aa{6G&5WKH@oR=#|Kp(#9GFo#t)Ya*O>)npd zU$`*;n~;C)cK$D<`_{FC>V7X|m%QP>DisOTCQ3=h5Cz|l?X*m7jza74yMKzGmVx~} zp=;Btt;j_`X+xTst8%7e7y%oD&i&f*#1F9_A>_uDHX`LRk!^;UjYbztyU-C~oMe&oD5_{2Nfsb5r1kTA0eSgU8YcHs}=q1b@ zt3$e3FHX|yP=JxFrXHFyiOvZX8g%}PL{)KT%+H+0JRKQ^FSln)(Aj{y4^j&T!NXms zv@UNH2J1678@8vxU;m8=<9~}l@4tsMXE+V&kB#$eq$XjHB4_PxPt41Dmg6=#Jp=0{ z+sE@08bH$U*T1-eL3Aj+$#VP__RCfnK2NE4M+UV;dl{Fyk+WcQ&!71@(CIfh5uyJJ zt^ej~d2ZPVo($@@)jxHkm@GBd+2Tsn*eNxuakL4F15SQXE5qlX+rs)j0i085pgu1UH9I7NCUxLVl&Z2HMOF8@jqTXMVT<(vTs z<~X(8&oqm?4wYum4O4)nT>RW0HmqM~sxGvk>P7jX#C=1xMD)}mu4RY|=e~0#RD6rA z0B-8~1_8=4+78mpl^rccZx1baxRv)qz~5;Ts_`E1o&85#CR8JVPcOY+^K`)1h~&mS zW(0^rpTz62FT~pT$)Q%<_xfGa+0~QSh14>`6o!_1;Ym1nBq#O()zU>xrQ`k3QSaTM z%rSseFXf1rW#G>(yzPl{mw~3jk~O7u8m`{^&)xez0bMV-GE;gg81}j*5HHb{gSJw1 z_0=E}EPe}#rwJu665OrMnaG%RM#kKkS#EO%;x*8o~Zezvr%rxDG!< z-|?Ork-&O0w`k<$1S$=9{2^6(4F0CgzaUFi!!MrTy77lZ*rDIKbN??~D-9 z#+F$6M_a51CJ7x4)$ByI-7n63!5qSG6KgedCp)2?IlL+l?`gG6e4oC2_5f-U*$;ZY z=t4EZW>HLBoyc4=eN;)N2DR-D&~V`#MS`T-Wj9~-qqfsFN7OtAR~JRQ5w z9fY5ARlz69)f*E~k;O+hABFWA#un8Urv^Z{FezMVH{N&gDgNS}!#wf&W6r(vJ` z=>)T|5+t!n7sS314R^VVdE4*zgBpk56)U$SWZC^ssydmB6iTW5c@ND48;LYM?LmU5 zsu?OO66VVE^7-7^z2C+}QZBcy#eFY@I`S~<*b02oJY|1l zDFk-)36np5>O|B9JZT!os*q{$nOdzG>?606n_m)+24baf&ES^`0PS50tSUrUP3cK!;NG`?R1!x?NcwJFfHzMWD#-L5&XtZuV|}5U3kH}!7;`Y25ZnuUi`q&I^17kv z)FJI-%fo2arq*XSTPbL-(#cGQkzk9rO{2Z50b+9Z`aF8x1m5{J@3~LKA>wtz!Z*2O zL{a^!chb@l-p-zH(|j6Zo>%FL0HS`=g8yM}bytV*&_F@j6 z1#Kv#tE83sLMM7Pr7tq@paxlcoT~rEi1&U;?TX&9udv!mehElp!DZ7Ccw(x>OVhW#LMPa7%&ih-1P@R=STo(q}##pm*n z;g`ls)kl@((0xqc&@KuET-_g)o_IfutpC%e-QO{WY&*4iTGm>TLPElX)?hC>(JMMq z=H7}RvB%}ANFQiW$zPBW%LJKye9}~ISWn$%cK)J08691=8ZtZ44z5wbcLR&aFw5Q< z);y1O;l@-?54KGq)7?uEPRnyJbL2R)pJ)zLiof>cIx~n0uH1T1)mMl%J346gp2oh( z9||9j?&?9>J-5V|1FO(^efbW?mt^=i_Idc4GxmXGjxAp*TLBMU|B<})aS(H~U`~o$ zKsnqF(%rK|C|1U|$Nfe>_>Jpz(NggKA$)-I5!T(_K$-q)YB*;;wC-&I3w|H6PS8Y= zypY^0>wlhDPt$7h^XSG=d&D7KxM$XG5Wd~tVr|_Y2de?b$sY2fu%WU$v8ZkWXjLWK z4)K-ZUCu=GhQU0XQL-o-xm5~P<{g3`9HyWlh}Cc)y$D@PQkNP(HwV@Ce`drdwxip^ zhXiI;T46JI-@=EbX85-7?gs1WIP@TOnj=+U0g~TZ9VWacLJpIqno`p+`nS~h^14n7 z(r#6rF#J4$&emOUCX3d<>F&KdcaF{=iHf97hdTsl7HvqA!@eG&JICG~*-t_DG?_Yi zPmKW~=Oy2_)MD6=uJ_B}^ZxFSE|$ZXlV*N8khabd=LWR)HB$r!;ciPjU+i2l$T;#f zSxff=eZ7?D&x3P_%i-)xS(!0Ns-2Fl;F?EvTXdhq6Nso${yf8^Ln++T*fvzI7=q$s zqN4lozUvX;>z7fX5$Fn#mYa+mL#ao1TN83iki~$C@`h#+@J@4!AIh%eX(f zSEet!Q(O+Vzeh!+@&2m$VTA^)fzan*x<)9}vTFWZe2O!AwOiZZs5FBrMdu5EP z1cgL=C*8w7?|&a}FTQ;-0n~%ev*LJe^t8h1r~P0)Qt(cVdmvwgy6iZ|3{^=GDZ9gr zGRsh{mQDlX$w|~+^2q%}IvI^h9V>ftodT~DUnZH1v;p$BV0*}gxq#+{Kc>+tx?I#9 z@4mbM;q5Y9mtY=-$1U@(*f&6)Zn&RlV?E;dFeoGOGyzH4D%O^KXhmioanH{*jl)!} za%D3Rpq_TplqDM$?>f{fpFIYyb;}r znvi+UtA>y0fN@b-T+b+I!=A+j)<)p=6!D`o97e@r``^Tw4ghm~VdGD%!l{!{GVfBXU0 zgp1vDN9Q51=W8_s?wjVRm5#k2kkB6^pJUqlXFmZw<; zIL5!*=6lqOUeT!u=50*D-5If4Vf-VIYZ{K;UM8a~!KCvYm=l!(o%epn&qIWW!_9@2 z3}~Cx^_o43eZ4P=6d18TcE3olm^tRK_MOcdyUaEQ2B#J0bq@O@;`#fVMOS-)&7^QY zjEq88&%f!A%uIC5+|HzuuMGL#ocOSGsT=6rHO|)cltRnC@m&oYI4`p-fN83k1R0|d z>%8x~(L9T~dw_2xidU?;DpJvk0)EPh=-elP?VnF^bwQEH?jPq~L;e!5v88!CN;`x6 zJQ8~v!~%ft4mn8mR1Hdf$et&H>#*s+S$8hsx?ksAfXOeZPH5TtkeD$)33fd%qtf{+ zKu2C>PV&wy+$uWJ>;1bKm^@U%EwACa0Ga|RrjtP7JULDl%tV%b#Pn*c3wL?iu9Kfd zf@^l7Rd2_LNOJwj{U*OYkiWcFOdjVcCrIArdO4MXzK&el`TdND#M-Z&r^a>fb)Ac6 zT$vhS_f>&6FY#RWbld*T>hNI@B;DMzRf0K6UBWb7mr~J_xCqf$1dL_-FyCSqOY|9of`+mQwafuf4TvVhlbZ-kl{p54!Wl z!aq;k4r)nnN(vXdP=zJ$O^w&pP?E8BW7qp8G$WA{JX%nL9>%o%Gaau-v<&AAmaU1% zszvdO>A~M%yF2WiL2@HBAOCoa*%NcHr0R&FIG?2Bxryq!AigiDOy-ZD9zs14hxg7E zcR;|)Ag1bXl~4^=h0gmdz;Lqyoh8QzoYh;DuGbxbg5a-h;*N7Ly2muwa<&qkjFGO7 z!hJE@>Jurt+(|IhaZoG$+=X;ZMHa(G`cQGx`T>i%d2~Pbqq@<{31Gi3wg{wt^l0Yh zhJV{{B$;+1?^os|($nmIX7H>ErBXLsQ7M=N<$cd2J7$Mr*X$C{0etTX@~HNG#s1(4 z8XK3;qEV=+S*tJ>0mx@GX570ngRCDu(BAvA0;HMmNyUEZ0M%4yW5MTX$Wi@-Dp70( zRJE2Rv`8&5dnkp)dA%IW35i07b5o%`KJ9ys9H8DyVO0t$^Pmy&@w6BA|0SP%!6z&C z3$BN}1B0_6NSNbT24lqt#2dT1z5Z5<{@|UWOzKf$T86#$dCeO)Mlx* z7?#k7_4wj@FYCaTv2#_Hste7DeThWsW$>EeSM$ZtE+9+TdA$$D`ZX8^ASN$=Dmxp!{OiywbSne-Ii>vJ(jY@JdqL9u|72mq zFWf0GEf;cHMWnvtxg}%zWy-E8%!^}xwse)82?IA-RC7bIzV7PP#N`DNe9lUYdWyM^ zx807cMO9aTRq!a0X*35NEo%{pf4%^<`c*ebDRt1l?k?2GF^XPHto`ohT}C1NHX2UR zVj4LHz@9hrIYE3$=so4YbH3xs* zE{p;3uWt()eQ;jy+~|pun$uAFxH)FamjM4w$D}i1{vBHt-Tn(=)3BECA4C628?-CB zTwLxZ0KK125ow|hXxNt&cJOnNSE;isjrVZ^y+|+o@)Ec&6w?KhMv?Z8*1WNhDoR?ylIDxPuZKo90ZI2KVH zBr9~`hrEv9G$@4lYxPulZdErN0!lW0{9=iTWC-2 zL83=3!gj7VfFXS&%Oy>$lQikP=q6K)c#pju7&$);MXBe;JU-Qe2p3UF*mVX3x#_<+ zmz4oyOIvfa@F;SlgdE+gGYzwMyRYvKCV-Ciu8H|1+&A++{`+o4KQVuCSwcdlr3HSx z{%tnP+l~mGKKa&oKi9I??u@3@6mm+AwY$VH1H#Wd-zXm;gUiSjuXeX)Xn1s`RCS>X z$z4gdVpF5QqH_J04V-hii>>i_wK5UZ(q>ePLPmj~(^N$wrx-oW)RMX$G67oV#Nb5T zQdrWT+Uxmj5*+-tjBbX0M=S#!xzXN>DDuS|ql6~`Ja=w2{2XgXrID$}=W(u7v@*c zeD7xF&>et({&7Z@vv_;Mcu@Y`Z9wny3?33z{^9(rVk9_&%V z=O_Hmb6)L*xnBnq{J1KC<$Ofq;>U61<;`-=BC!+Q$;~=S`?m+ys5A~6$@hTJLDH8x z%-!P9$~xoCoQ?u^FB7ZW=Ar12NC~-Y6r{WkoDjH|1FA(b=-8JZ@YL#O`wJ~ zcI}T$1SIFAtKaR3@4rWKF5Pc>k>XjNKPCsO(I4fU{eI%*xDFjnDWVY2i1=3#Sr?q& zbI&KZKOqnO>9TTRpUDIi8P9rSViq-C0sZ*H3y@a7HTH_N3bM}seOE=qdgLi$!;{mK zkftk1H8eg7MmC_svsw?~mS%i;R3nhTU2xJmrUu=aJRnHG^|`TMtsZvtYmZ*y_aS|0hO##sf|sbw6BuIAS*6-< zcXQ@YPj1~LdHN5WyPxGf*I5Snx`plL4J9CJ7}96g)rR^CdtS(IjlV2~A>kVOsE7U<4w?wOBP8iD06V_?Ek- z7o|K|raktfAEb8eKKN;Y0Hw{o1!pl|Ms2TLv(h*Lkx8NXCPvFBfQQWdHW=$M%$LLT z*GfQ(CvcBUb30=7I(Xq|`wSf1p3{!nm<91su0pBcaTL+PZ_1cj3t`VM3TA|tL7B6P z0$pDzDtmFLeeep-m8(hoeX__8ruMQkmb{sQlHc2JrN8%rlb!2OPb>*4C0S>5TKb{v zmqnA_Vh6IKA{YKJ9S5uE$jySx8L;t~RiTuTz}_Xmjb~vJ&b9V)78Z4Zl8~5LUoYlT zI)CF>7@k4BPdz!}l#0-u;)%>FeAxGQ!K*5@pb2PeDjIDt=XI~<)pz=KV@Pe(W`}aD z2Qqk-xqYQFq4Oj6@4+h(miytYLMp)PKI9!4KP7et^+Wy;I&s_(h^d6f=|8SpIxDwO&Zw$!S zE`F7y#q;x9O3FSRq4;^6NH}o<^QQ9;L|iDJ1s}5ECu;R2aDf=H*E@i|P!_LzG73j- zbHyRoeCL5E=kVoF-#Yf4${*5wgmolL(gR@&^Kip+Gt{bX9B#^4%er0eLriYDw^OiB z{?+b1*Bv+~z-w>z%{PU0klJv&cx~Stda9$YXtRyIQ(?}YyqM#0>1f=5)Tau#-P@;& zh3ioFZZDeyR~MA52~5|gVxAlA!q4H14A^xd*UF&}>z+!v=Xf5Cf?s61@46ciwnBBR z`lYdtvFle~C)Nco`o{IvPxPW1!(D5lvF)Hv+Uj0uBcRqjbsqAOttibty}{vW1Iqc{ zKd~~Of)f6%JUX(s1Qt!(n-UynL4WmMTxfI#63I?nZ>p&R-dFF!ny{~xrRT#=&YvVS za8RIrG6v^&m&>TuwG+^B4&zJW0dsKDenm&Q1M}6#NIsfLBp7vvfYbC?uPT0oX6sxf zocqw&Ve_vKjW=m%sTW7UH<%1fE2#p`zr~|Q*q41=H+lSW+XNi4NuGFw-~S1hg=d}h z6G%T`D%wgp6^u=#4jJ#q=hunz>_n9=;B|=lw_BwY_)a_{rwUY{bA#Fr)8Zq@U8lLL zJA4Q(zEz>~il#sdG3LSaVVo<95v)PKMm-U(W?)wVn=Bk;i|uSGy; z5uV!5@O;Db*OjIhL8D@Wx=$dx*w<%h;S ze%#|I;lSsj(}r^pBl>Jf^8g@rwYh^9(>Omujnpe$H~_~r&^Y6fA!zBAmEGYT0i_N^ zC7V5lMr`_*ggHkLmEQhKQl49I;`7n#Tm}UQWxQ@3f4T)n&-^g?a24|vVunhs_-D~y zhNAS?tSR_xck>$4qbamYm|9%sNee2FO>v!^-T<#E=Pb`CFoR_ibJs)6y%*4+^ zlZi~^g5!P@ zDY@=-l=M%4u>bKgEE(T)${KD*XWzXuyZ@X39|JDFltaU)mILJvbFZSxW`k``tas5+ zyTPpayb}UrE?OU$nt)l>(uWzf^PvC4L{cnz9LB$nQ+;X90R89JL;ppU!pt#=_yhar z(0}9CK&w9i<}_1}ZCWgV(Q^8C9?Y$KLMN${ZQKlr8$txHkFzk*Ne@RhmyohewaX9( zp0h5WR%f$rMH=4-?$bEGX8%r`W$gQPFxj<}HCUaC#%i17*f;u6rO@s#-+4wMS@GfZ z%A{9t{A18QH)S%)?cmyzW7&$7ZR>_3J@b+M4L#}jo87?AL3Od65${un=jn1T=0k)* z6_)prmm#}VYALGt!M&GLiv-Ef+AcnSPz|w54=-`W;Qgk2Yh%c0 zB^+8Y+#pkLBJTO-9}HOM-dAKswnqFqby(Bje(5 za487)3GE~BO(CtzKS#%~FUY3h^YOd5Ze{Xz-*RCcL3AV3g>$z9{wd zIq4Fh-O~2(@En4ED;}DyYt`Vjn=4L(xCLTK=S{aq$!LzSsQl8U2W@8dF2>qULhUNs z!jZ@(pj+MwAZksZOg(FdF?@jly-SVQP}bR)4$k0yad_{f@d zZY%UAu8ur--wEE`Uss#DGN2)OMcSUJ9yng>dg{n^!?2}!n^)};WIp=9OQbG8t|(B6ASz!}{E8#TSMAsmLVDx0>~8Aspx- z-k)yhN6mi+EU$SdqS?=ZNYJMfT{wT-?1N|*kd7-UMQ+a{?OX}nqw@2p;%#!Bac~VV zBsO_cP)t{%70KHHCN4jl8YUaGr4{*uoiQWO`dFLd zcButu#DW{%g|K5R9cj|1iic+s#^5mZxT3emFbI9Om~%JG0~NJL woQ%V*Ajx?^ #import +#import "TGMediaVideoConverter.h" #import #import #import @@ -2404,6 +2405,10 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus SSignal *thumbnailSignal = adjustments.trimStartValue > FLT_EPSILON ? trimmedVideoThumbnailSignal : videoThumbnailSignal; + TGMediaVideoConversionPreset preset = [TGMediaVideoConverter presetFromAdjustments:adjustments]; + CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset]; + NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : video.videoDuration; + [signals addObject:[thumbnailSignal map:^id(UIImage *image) { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; @@ -2411,6 +2416,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus dict[@"url"] = video.avAsset.URL; dict[@"previewImage"] = image; dict[@"adjustments"] = adjustments; + dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions]; + dict[@"duration"] = @(duration); if (adjustments.paintingData.stickers.count > 0) dict[@"stickers"] = adjustments.paintingData.stickers; diff --git a/LegacyComponents/TGMediaAssetsController.m b/LegacyComponents/TGMediaAssetsController.m index bd4d18d640..d9648d8ad5 100644 --- a/LegacyComponents/TGMediaAssetsController.m +++ b/LegacyComponents/TGMediaAssetsController.m @@ -749,6 +749,9 @@ NSArray *entities = [editingContext entitiesForItem:asset]; id adjustments = [editingContext adjustmentsForItem:asset]; + CGSize dimensions = asset.originalSize; + NSTimeInterval duration = asset.videoDuration; + [signals addObject:[inlineThumbnailSignal(asset) map:^id(UIImage *image) { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; @@ -757,6 +760,8 @@ dict[@"asset"] = asset; dict[@"previewImage"] = image; dict[@"fileName"] = asset.fileName; + dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions]; + dict[@"duration"] = @(duration); if (adjustments.paintingData.stickers.count > 0) dict[@"stickers"] = adjustments.paintingData.stickers; @@ -799,6 +804,10 @@ SSignal *thumbnailSignal = adjustments.trimStartValue > FLT_EPSILON ? trimmedVideoThumbnailSignal : videoThumbnailSignal; + TGMediaVideoConversionPreset preset = [TGMediaVideoConverter presetFromAdjustments:adjustments]; + CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset]; + NSTimeInterval duration = adjustments.trimApplied ? (adjustments.trimEndValue - adjustments.trimStartValue) : asset.videoDuration; + [signals addObject:[thumbnailSignal map:^id(UIImage *image) { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; @@ -807,6 +816,8 @@ dict[@"asset"] = asset; dict[@"previewImage"] = image; dict[@"adjustments"] = adjustments; + dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions]; + dict[@"duration"] = @(duration); if (adjustments.paintingData.stickers.count > 0) dict[@"stickers"] = adjustments.paintingData.stickers; diff --git a/LegacyComponents/TGMediaVideoConverter.h b/LegacyComponents/TGMediaVideoConverter.h index 0bea891602..a79246c6cc 100644 --- a/LegacyComponents/TGMediaVideoConverter.h +++ b/LegacyComponents/TGMediaVideoConverter.h @@ -23,6 +23,7 @@ + (TGMediaVideoConversionPreset)bestAvailablePresetForDimensions:(CGSize)dimensions; + (CGSize)_renderSizeWithCropSize:(CGSize)cropSize; ++ (TGMediaVideoConversionPreset)presetFromAdjustments:(TGMediaVideoEditAdjustments *)adjustments; + (CGSize)dimensionsFor:(CGSize)dimensions adjustments:(TGMediaVideoEditAdjustments *)adjustments preset:(TGMediaVideoConversionPreset)preset; @end diff --git a/LegacyComponents/TGMediaVideoConverter.m b/LegacyComponents/TGMediaVideoConverter.m index 8e6fdc0125..bce62cfcb7 100644 --- a/LegacyComponents/TGMediaVideoConverter.m +++ b/LegacyComponents/TGMediaVideoConverter.m @@ -116,7 +116,7 @@ return; CGSize dimensions = [avAsset tracksWithMediaType:AVMediaTypeVideo].firstObject.naturalSize; - TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self _presetFromAdjustments:adjustments]; + TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self presetFromAdjustments:adjustments]; if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage) { TGMediaVideoConversionPreset bestPreset = [self bestAvailablePresetForDimensions:dimensions]; @@ -524,7 +524,7 @@ NSError *error; NSData *fileData = [NSData dataWithContentsOfURL:fileUrl options:NSDataReadingMappedIfSafe error:&error]; if (error == nil) - return [SSignal single:[self _hashForVideoWithFileData:fileData timingData:timingData preset:[self _presetFromAdjustments:adjustments]]]; + return [SSignal single:[self _hashForVideoWithFileData:fileData timingData:timingData preset:[self presetFromAdjustments:adjustments]]]; else return [SSignal fail:error]; } @@ -572,7 +572,7 @@ return hash; } -+ (TGMediaVideoConversionPreset)_presetFromAdjustments:(TGMediaVideoEditAdjustments *)adjustments ++ (TGMediaVideoConversionPreset)presetFromAdjustments:(TGMediaVideoEditAdjustments *)adjustments { TGMediaVideoConversionPreset preset = adjustments.preset; if (preset == TGMediaVideoConversionPresetCompressedDefault) diff --git a/LegacyComponents/TGPassportAttachMenu.h b/LegacyComponents/TGPassportAttachMenu.h index 5e31dc1fce..f46efc5afd 100644 --- a/LegacyComponents/TGPassportAttachMenu.h +++ b/LegacyComponents/TGPassportAttachMenu.h @@ -4,8 +4,16 @@ @class TGViewController; @class TGMenuSheetController; +typedef enum +{ + TGPassportAttachIntentDefault, + TGPassportAttachIntentIdentityCard, + TGPassportAttachIntentSelfie, + TGPassportAttachIntentMultiple +} TGPassportAttachIntent; + @interface TGPassportAttachMenu : NSObject -+ (TGMenuSheetController *)presentWithContext:(id)context parentController:(TGViewController *)parentController menuController:(TGMenuSheetController *)menuController title:(NSString *)title identity:(bool)identity selfie:(bool)selfie uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect barButtonItem:(UIBarButtonItem *)barButtonItem; ++ (TGMenuSheetController *)presentWithContext:(id)context parentController:(TGViewController *)parentController menuController:(TGMenuSheetController *)menuController title:(NSString *)title intent:(TGPassportAttachIntent)intent uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect barButtonItem:(UIBarButtonItem *)barButtonItem; @end diff --git a/LegacyComponents/TGPassportAttachMenu.m b/LegacyComponents/TGPassportAttachMenu.m index f07ebf15e3..e55887a2d5 100644 --- a/LegacyComponents/TGPassportAttachMenu.m +++ b/LegacyComponents/TGPassportAttachMenu.m @@ -27,7 +27,7 @@ @implementation TGPassportAttachMenu -+ (TGMenuSheetController *)presentWithContext:(id)context parentController:(TGViewController *)parentController menuController:(TGMenuSheetController *)menuController title:(NSString *)title identity:(bool)identity selfie:(bool)selfie uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect barButtonItem:(UIBarButtonItem *)barButtonItem ++ (TGMenuSheetController *)presentWithContext:(id)context parentController:(TGViewController *)parentController menuController:(TGMenuSheetController *)menuController title:(NSString *)title intent:(TGPassportAttachIntent)intent uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction sourceView:(UIView *)sourceView sourceRect:(CGRect (^)(void))sourceRect barButtonItem:(UIBarButtonItem *)barButtonItem { if (uploadAction == nil) return nil; @@ -51,7 +51,7 @@ __weak TGMenuSheetController *weakController = controller; __weak TGViewController *weakParentController = parentController; - TGAttachmentCarouselItemView *carouselItem = [[TGAttachmentCarouselItemView alloc] initWithContext:context camera:true selfPortrait:selfie forProfilePhoto:false assetType:TGMediaAssetPhotoType saveEditedPhotos:false allowGrouping:false document:true]; + TGAttachmentCarouselItemView *carouselItem = [[TGAttachmentCarouselItemView alloc] initWithContext:context camera:true selfPortrait:intent == TGPassportAttachIntentSelfie forProfilePhoto:false assetType:TGMediaAssetPhotoType saveEditedPhotos:false allowGrouping:false document:true]; __weak TGAttachmentCarouselItemView *weakCarouselItem = carouselItem; carouselItem.onlyCrop = true; carouselItem.parentController = parentController; @@ -65,7 +65,7 @@ if (strongParentController == nil) return; - [TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context identity:identity uploadAction:uploadAction]; + [TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context intent:intent uploadAction:uploadAction]; }; carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles) { @@ -99,7 +99,7 @@ }]; [itemViews addObject:galleryItem]; - if (iosMajorVersion() >= 8 && !selfie) + if (iosMajorVersion() >= 8 && intent != TGPassportAttachIntentSelfie) { TGMenuSheetButtonItemView *icloudItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.FileICloudDrive") type:TGMenuSheetButtonTypeDefault action:^ { @@ -270,7 +270,7 @@ [parentController presentViewController:legacyCameraController animated:true completion:nil]; } -+ (void)_displayCameraWithView:(TGAttachmentCameraView *)cameraView menuController:(TGMenuSheetController *)menuController parentController:(TGViewController *)parentController context:(id)context identity:(bool)identity uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction ++ (void)_displayCameraWithView:(TGAttachmentCameraView *)cameraView menuController:(TGMenuSheetController *)menuController parentController:(TGViewController *)parentController context:(id)context intent:(bool)intent uploadAction:(void (^)(SSignal *, void (^)(void)))uploadAction { if (![[[LegacyComponentsGlobals provider] accessChecker] checkCameraAuthorizationStatusForIntent:TGCameraAccessIntentDefault alertDismissCompletion:nil]) return; @@ -291,9 +291,9 @@ id windowManager = [context makeOverlayWindowManager]; if (cameraView.previewView != nil) - controller = [[TGCameraController alloc] initWithContext:[windowManager context] saveEditedPhotos:false saveCapturedMedia:false camera:cameraView.previewView.camera previewView:cameraView.previewView intent:identity ? TGCameraControllerPassportIdIntent : TGCameraControllerPassportIntent]; + controller = [[TGCameraController alloc] initWithContext:[windowManager context] saveEditedPhotos:false saveCapturedMedia:false camera:cameraView.previewView.camera previewView:cameraView.previewView intent:intent == TGPassportAttachIntentIdentityCard ? TGCameraControllerPassportIdIntent : TGCameraControllerPassportIntent]; else - controller = [[TGCameraController alloc] initWithContext:[windowManager context] saveEditedPhotos:false saveCapturedMedia:false intent:identity ? TGCameraControllerPassportIdIntent : TGCameraControllerPassportIntent]; + controller = [[TGCameraController alloc] initWithContext:[windowManager context] saveEditedPhotos:false saveCapturedMedia:false intent:intent == TGPassportAttachIntentIdentityCard ? TGCameraControllerPassportIdIntent : TGCameraControllerPassportIntent]; controller.shouldStoreCapturedAssets = false; diff --git a/LegacyComponents/TGPassportMRZ.h b/LegacyComponents/TGPassportMRZ.h new file mode 100644 index 0000000000..ee41fc30fe --- /dev/null +++ b/LegacyComponents/TGPassportMRZ.h @@ -0,0 +1,25 @@ +#import + +@interface TGPassportMRZ : NSObject + +@property (nonatomic, readonly) NSString *documentType; +@property (nonatomic, readonly) NSString *documentSubtype; +@property (nonatomic, readonly) NSString *issuingCountry; +@property (nonatomic, readonly) NSString *lastName; +@property (nonatomic, readonly) NSString *firstName; +@property (nonatomic, readonly) NSString *documentNumber; +@property (nonatomic, readonly) NSString *nationality; +@property (nonatomic, readonly) NSDate *birthDate; +@property (nonatomic, readonly) NSString *gender; +@property (nonatomic, readonly) NSDate *expiryDate; +@property (nonatomic, readonly) NSString *optional1; +@property (nonatomic, readonly) NSString *optional2; + +@property (nonatomic, readonly) NSString *mrz; + ++ (instancetype)parseLines:(NSArray *)lines; + +@end + +extern const NSUInteger TGPassportTD1Length; +extern const NSUInteger TGPassportTD23Length; diff --git a/LegacyComponents/TGPassportMRZ.m b/LegacyComponents/TGPassportMRZ.m new file mode 100644 index 0000000000..fa971d795d --- /dev/null +++ b/LegacyComponents/TGPassportMRZ.m @@ -0,0 +1,316 @@ +#import "TGPassportMRZ.h" + +const NSUInteger TGPassportTD1Length = 30; +const NSUInteger TGPassportTD23Length = 44; +NSString *const TGPassportEmptyCharacter = @"<"; + +@implementation TGPassportMRZ + ++ (instancetype)parseLines:(NSArray *)lines +{ + if (lines.count == 2) + { + if (lines[0].length != TGPassportTD23Length || lines[1].length != TGPassportTD23Length) + return nil; + + TGPassportMRZ *result = [[TGPassportMRZ alloc] init]; + result->_documentType = [lines[0] substringToIndex:1]; + result->_documentSubtype = [self cleanString:[lines[0] substringWithRange:NSMakeRange(1, 1)]]; + result->_issuingCountry = [self cleanString:[lines[0] substringWithRange:NSMakeRange(2, 3)]]; + + NSCharacterSet *emptyCharacterSet = [NSCharacterSet characterSetWithCharactersInString:TGPassportEmptyCharacter]; + NSString *fullName = [[lines[0] substringWithRange:NSMakeRange(5, 39)] stringByTrimmingCharactersInSet:emptyCharacterSet]; + NSArray *names = [fullName componentsSeparatedByString:@"<<"]; + result->_lastName = [self nameString:names.firstObject]; + result->_firstName = [self nameString:names.lastObject]; + + NSString *documentNumber = [self ensureNumberString:[lines[1] substringToIndex:9]]; + NSInteger documentNumberCheck = [[self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(9, 1)]] integerValue]; + if ([self isDataValid:documentNumber check:documentNumberCheck]) + result->_documentNumber = documentNumber; + + result->_nationality = [lines[1] substringWithRange:NSMakeRange(10, 3)]; + NSString *birthDate = [self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(13, 6)]]; + NSInteger birthDateCheck = [[self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(19, 1)]] integerValue]; + if ([self isDataValid:birthDate check:birthDateCheck]) + result->_birthDate = [self dateFromString:birthDate]; + + NSString *gender = [lines[1] substringWithRange:NSMakeRange(20, 1)]; + if ([gender isEqualToString:TGPassportEmptyCharacter]) + gender = nil; + result->_gender = gender; + + NSString *expiryDate = [self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(21, 6)]]; + NSInteger expiryDateCheck = [[self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(27, 1)]] integerValue]; + if ([self isDataValid:expiryDate check:expiryDateCheck]) + result->_expiryDate = [self dateFromString:expiryDate]; + + NSString *optional1 = [lines[1] substringWithRange:NSMakeRange(28, 14)]; + NSString *optional1CheckString = [self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(42, 1)]]; + NSInteger optional1CheckValue = [optional1CheckString isEqualToString:TGPassportEmptyCharacter] ? 0 : [optional1CheckString integerValue]; + if ([self isDataValid:optional1 check:optional1CheckValue]) + result->_optional1 = [self cleanString:optional1]; + + NSString *data = [NSString stringWithFormat:@"%@%d%@%d%@%d%@%@", documentNumber, (int)documentNumberCheck, birthDate, (int)birthDateCheck, expiryDate, (int)expiryDateCheck, optional1, optional1CheckString]; + NSInteger dataCheck = [[self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(43, 1)]] integerValue]; + if ([self isDataValid:data check:dataCheck]) + { + if ([result->_documentType isEqualToString:@"P"] && [result->_documentSubtype isEqualToString:@"N"] && [result->_issuingCountry isEqualToString:@"RUS"]) + { + NSString *lastName = [self transliterateRussianMRZString:result->_lastName]; + result->_lastName = [self transliterateRussianName:lastName]; + NSString *firstName = [self transliterateRussianMRZString:result->_firstName]; + result->_firstName = [self transliterateRussianName:firstName]; + + NSString *lastSeriesDigit = [optional1 substringToIndex:1]; + NSMutableString *fullDocumentNo = [[NSMutableString alloc] init]; + [fullDocumentNo insertString:[result->_documentNumber substringToIndex:3] atIndex:0]; + [fullDocumentNo appendString:lastSeriesDigit]; + [fullDocumentNo appendString:[result->_documentNumber substringFromIndex:3]]; + result->_documentNumber = fullDocumentNo; + } + + + result->_mrz = [lines componentsJoinedByString:@"\n"]; + return result; + } + } + else if (lines.count == 3) + { + if (lines[0].length != TGPassportTD1Length || lines[1].length != TGPassportTD1Length || lines[2].length != TGPassportTD1Length) + return nil; + + TGPassportMRZ *result = [[TGPassportMRZ alloc] init]; + result->_documentType = [lines[0] substringToIndex:1]; + result->_documentSubtype = [self cleanString:[lines[0] substringWithRange:NSMakeRange(1, 1)]]; + result->_issuingCountry = [self cleanString:[lines[0] substringWithRange:NSMakeRange(2, 3)]]; + + NSString *documentNumber = [self ensureNumberString:[lines[0] substringWithRange:NSMakeRange(5, 9)]]; + NSInteger documentNumberCheck = [[self ensureNumberString:[lines[0] substringWithRange:NSMakeRange(14, 1)]] integerValue]; + if ([self isDataValid:documentNumber check:documentNumberCheck]) + result->_documentNumber = documentNumber; + + NSString *optional1 = [lines[0] substringWithRange:NSMakeRange(15, 15)]; + result->_optional1 = [self cleanString:optional1]; + + NSString *birthDate = [self ensureNumberString:[lines[1] substringToIndex:6]]; + NSInteger birthDateCheck = [[lines[1] substringWithRange:NSMakeRange(6, 1)] integerValue]; + if ([self isDataValid:birthDate check:birthDateCheck]) + result->_birthDate = [self dateFromString:birthDate]; + + NSString *gender = [lines[1] substringWithRange:NSMakeRange(7, 1)]; + if ([gender isEqualToString:TGPassportEmptyCharacter]) + gender = nil; + result->_gender = gender; + + NSString *expiryDate = [self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(8, 6)]]; + NSInteger expiryDateCheck = [[self ensureNumberString:[lines[1] substringWithRange:NSMakeRange(14, 1)]] integerValue]; + if ([self isDataValid:expiryDate check:expiryDateCheck]) + result->_expiryDate = [self dateFromString:expiryDate]; + + result->_nationality = [lines[1] substringWithRange:NSMakeRange(15, 3)]; + + NSString *optional2 = [lines[1] substringWithRange:NSMakeRange(18, 11)]; + result->_optional2 = optional2; + + NSCharacterSet *emptyCharacterSet = [NSCharacterSet characterSetWithCharactersInString:TGPassportEmptyCharacter]; + NSString *fullName = [self ensureAlphaString:lines[2]]; + fullName = [fullName stringByTrimmingCharactersInSet:emptyCharacterSet]; + NSArray *names = [fullName componentsSeparatedByString:@"<<"]; + result->_lastName = [self nameString:names.firstObject]; + result->_firstName = [self nameString:names.lastObject]; + result->_mrz = [lines componentsJoinedByString:@"\n"]; + + return result; + } + return nil; +} + ++ (NSDateFormatter *)dateFormatter +{ + static dispatch_once_t onceToken; + static NSDateFormatter *dateFormatter; + dispatch_once(&onceToken, ^ + { + dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"YYMMdd"; + dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + }); + return dateFormatter; +} + + ++ (NSDate *)dateFromString:(NSString *)string +{ + return [[self dateFormatter] dateFromString:string]; +} + ++ (NSString *)cleanString:(NSString *)string +{ + return [string stringByReplacingOccurrencesOfString:TGPassportEmptyCharacter withString:@""]; +} + ++ (NSString *)nameString:(NSString *)string +{ + return [string stringByReplacingOccurrencesOfString:TGPassportEmptyCharacter withString:@" "]; +} + ++ (NSString *)ensureNumberString:(NSString *)string +{ + return [[[[string stringByReplacingOccurrencesOfString:@"O" withString:@"0"] stringByReplacingOccurrencesOfString:@"U" withString:@"0"] stringByReplacingOccurrencesOfString:@"Q" withString:@"0"] stringByReplacingOccurrencesOfString:@"J" withString:@"0"]; +} + ++ (NSString *)ensureAlphaString:(NSString *)string +{ + NSCharacterSet *validChars = [NSCharacterSet characterSetWithCharactersInString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZ<"]; + NSCharacterSet *invalidChars = [validChars invertedSet]; + + string = [string stringByReplacingOccurrencesOfString:@"0" withString:@"O"]; + return [self string:string byReplacingCharactersInSet:invalidChars withString:TGPassportEmptyCharacter]; +} + ++ (NSString *)string:(NSString *)string byReplacingCharactersInSet:(NSCharacterSet *)charSet withString:(NSString *)aString { + NSMutableString *s = [NSMutableString stringWithCapacity:string.length]; + for (NSUInteger i = 0; i < string.length; ++i) { + unichar c = [string characterAtIndex:i]; + if (![charSet characterIsMember:c]) { + [s appendFormat:@"%C", c]; + } else { + [s appendString:aString]; + } + } + return s; +} + ++ (NSString *)transliterateRussianMRZString:(NSString *)string +{ + NSDictionary *map = @ + { + @"A": @"А", + @"B": @"Б", + @"V": @"В", + @"G": @"Г", + @"D": @"Д", + @"E": @"Е", + @"2": @"Ё", + @"J": @"Ж", + @"Z": @"З", + @"I": @"И", + @"Q": @"Й", + @"K": @"К", + @"L": @"Л", + @"M": @"М", + @"N": @"Н", + @"O": @"О", + @"P": @"П", + @"R": @"Р", + @"S": @"С", + @"T": @"Т", + @"U": @"У", + @"F": @"Ф", + @"H": @"Х", + @"C": @"Ц", + @"3": @"Ч", + @"4": @"Ш", + @"W": @"Щ", + @"X": @"Ъ", + @"Y": @"Ы", + @"9": @"Ь", + @"6": @"Э", + @"7": @"Ю", + @"8": @"Я", + @" ": @" " + }; + + NSMutableString *result = [[NSMutableString alloc] init]; + [string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop) + { + if (substring == nil) + return; + + NSString *letter = map[substring]; + if (letter != nil) + [result appendString:letter]; + }]; + return result; +} + ++ (NSString *)transliterateRussianName:(NSString *)string +{ + NSDictionary *map = @ + { + @"А": @"A", + @"Б": @"B", + @"В": @"V", + @"Г": @"G", + @"Д": @"D", + @"Е": @"E", + @"Ё": @"E", + @"Ж": @"ZH", + @"З": @"Z", + @"И": @"I", + @"Й": @"I", + @"К": @"K", + @"Л": @"L", + @"М": @"M", + @"Н": @"N", + @"О": @"O", + @"П": @"P", + @"Р": @"R", + @"С": @"S", + @"Т": @"T", + @"У": @"U", + @"Ф": @"F", + @"Х": @"KH", + @"Ц": @"TS", + @"Ч": @"CH", + @"Ш": @"SH", + @"Щ": @"SHCH", + @"Ъ": @"IE", + @"Ы": @"Y", + @"Ь": @"", + @"Э": @"E", + @"Ю": @"IU", + @"Я": @"IA", + @" ": @" " + }; + + NSMutableString *result = [[NSMutableString alloc] init]; + [string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop) + { + if (substring == nil) + return; + + NSString *letter = map[substring]; + if (letter != nil) + [result appendString:letter]; + }]; + return result; +} + ++ (bool)isDataValid:(NSString *)data check:(NSInteger)check +{ + int32_t sum = 0; + uint8_t w[3] = { 7, 3, 1 }; + + for (NSUInteger i = 0; i < data.length; i++) + { + unichar c = [data characterAtIndex:i]; + NSInteger d = 0; + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'A' && c <= 'Z') + d = (10 + c) - 'A'; + else if (c != '<') + return false; + + sum += d * w[i % 3]; + } + + if (sum % 10 != check) + return false; + + return true; +} + +@end diff --git a/LegacyComponents/TGPassportOCR.h b/LegacyComponents/TGPassportOCR.h new file mode 100644 index 0000000000..c6909660c3 --- /dev/null +++ b/LegacyComponents/TGPassportOCR.h @@ -0,0 +1,8 @@ +#import +#import + +@interface TGPassportOCR : NSObject + ++ (SSignal *)recognizeMRZInImage:(UIImage *)image; + +@end diff --git a/LegacyComponents/TGPassportOCR.mm b/LegacyComponents/TGPassportOCR.mm new file mode 100644 index 0000000000..df158eb9b9 --- /dev/null +++ b/LegacyComponents/TGPassportOCR.mm @@ -0,0 +1,21 @@ +#import "TGPassportOCR.h" +#import "TGPassportMRZ.h" +#import "ocr.h" + +@implementation TGPassportOCR + ++ (SSignal *)recognizeMRZInImage:(UIImage *)image +{ + return [[SSignal defer:^SSignal * + { + CGRect boundingRect; + NSString *string = recognizeMRZ(image, &boundingRect); + + NSArray *lines = [string componentsSeparatedByString:@"\n"]; + TGPassportMRZ *mrz = [TGPassportMRZ parseLines:lines]; + + return [SSignal single:mrz]; + }] startOn:[SQueue concurrentDefaultQueue]]; +} + +@end diff --git a/LegacyComponents/TGPassportScanView.h b/LegacyComponents/TGPassportScanView.h new file mode 100644 index 0000000000..406137adf9 --- /dev/null +++ b/LegacyComponents/TGPassportScanView.h @@ -0,0 +1,13 @@ +#import + +@class TGPassportMRZ; + +@interface TGPassportScanView : UIView + +@property (nonatomic, copy) void (^finishedWithMRZ)(TGPassportMRZ *); + +- (void)start; +- (void)stop; +- (void)pause; + +@end diff --git a/LegacyComponents/TGPassportScanView.m b/LegacyComponents/TGPassportScanView.m new file mode 100644 index 0000000000..81b70e14a4 --- /dev/null +++ b/LegacyComponents/TGPassportScanView.m @@ -0,0 +1,98 @@ +#import "TGPassportScanView.h" +#import "PGCamera.h" +#import "TGCameraPreviewView.h" + +#import "TGPassportOCR.h" + +#import "LegacyComponentsInternal.h" + +#import "TGTimerTarget.h" + +@interface TGPassportScanView () +{ + PGCamera *_camera; + TGCameraPreviewView *_previewView; + + NSTimer *_timer; + SMetaDisposable *_ocrDisposable; +} +@end + +@implementation TGPassportScanView + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + _camera = [[PGCamera alloc] initWithMode:PGCameraModePhoto position:PGCameraPositionRear]; + _previewView = [[TGCameraPreviewView alloc] initWithFrame:self.bounds]; + [self addSubview:_previewView]; + + [_camera attachPreviewView:_previewView]; + + _ocrDisposable = [[SMetaDisposable alloc] init]; + } + return self; +} + +- (void)dealloc +{ + [_ocrDisposable dispose]; +} + +- (void)start +{ + [_camera startCaptureForResume:false completion:nil]; + + _timer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(handleNextFrame) interval:1.0 repeat:false]; +} + +- (void)stop +{ + [_camera stopCaptureForPause:false completion:nil]; + _camera = nil; + + [_timer invalidate]; + _timer = nil; +} + +- (void)pause +{ + [_camera stopCaptureForPause:true completion:nil]; +} + +- (void)handleNextFrame +{ + __weak TGPassportScanView *weakSelf = self; + [_camera captureNextFrameCompletion:^(UIImage *image) + { + [_ocrDisposable setDisposable:[[[TGPassportOCR recognizeMRZInImage:image] deliverOn:[SQueue mainQueue]] startWithNext:^(TGPassportMRZ *next) + { + __strong TGPassportScanView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (next != nil) + { + [strongSelf->_camera stopCaptureForPause:true completion:nil]; + + if (strongSelf.finishedWithMRZ != nil) + strongSelf.finishedWithMRZ(next); + } + else + { + strongSelf->_timer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(handleNextFrame) interval:0.45 repeat:false]; + } + }]]; + }]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + _previewView.frame = self.bounds; +} + +@end diff --git a/LegacyComponents/fast-edge.cpp b/LegacyComponents/fast-edge.cpp new file mode 100755 index 0000000000..0e67626501 --- /dev/null +++ b/LegacyComponents/fast-edge.cpp @@ -0,0 +1,550 @@ +/* + FAST-EDGE + Copyright (c) 2009 Benjamin C. Haynor + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include "fast-edge.h" + +#define LOW_THRESHOLD_PERCENTAGE 0.8 // percentage of the high threshold value that the low threshold shall be set at +#define PI 3.14159265 +#define HIGH_THRESHOLD_PERCENTAGE 0.10 // percentage of pixels that meet the high threshold - for example 0.15 will ensure that at least 15% of edge pixels are considered to meet the high threshold + +#define min(X,Y) ((X) < (Y) ? (X) : (Y)) +#define max(X,Y) ((X) < (Y) ? (Y) : (X)) + +namespace ocr{ +/* + CANNY EDGE DETECT + DOES NOT PERFORM NOISE REDUCTION - PERFORM NOISE REDUCTION PRIOR TO USE + Noise reduction omitted, as some applications benefit from morphological operations such as opening or closing as opposed to Gaussian noise reduction + If your application always takes the same size input image, uncomment the definitions of WIDTH and HEIGHT in the header file and define them to the size of your input image, + otherwise the required intermediate arrays will be dynamically allocated. + If WIDTH and HEIGHT are defined, the arrays will be allocated in the compiler directive that follows: +*/ +#ifdef WIDTH +int g[WIDTH * HEIGHT], dir[WIDTH * HEIGHT] = {0}; +unsigned char img_scratch_data[WIDTH * HEIGHT] = {0}; +#endif +void canny_edge_detect(struct image * img_in, struct image * img_out) { + struct image img_scratch; + int high, low; + #ifndef WIDTH + int * g = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); + int * dir = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); + unsigned char * img_scratch_data = (unsigned char*)calloc(static_cast(img_in->width*img_in->height), sizeof(unsigned char)); + #endif + img_scratch.width = img_in->width; + img_scratch.height = img_in->height; + img_scratch.pixel_data = img_scratch_data; + calc_gradient_sobel(img_in, g, dir); + //printf("*** performing non-maximum suppression ***\n"); + non_max_suppression(&img_scratch, g, dir); + estimate_threshold(&img_scratch, &high, &low); + hysteresis(high, low, &img_scratch, img_out); + #ifndef WIDTH + free(g); + free(dir); + free(img_scratch_data); + #endif +} + +/* + GAUSSIAN_NOISE_ REDUCE + apply 5x5 Gaussian convolution filter, shrinks the image by 4 pixels in each direction, using Gaussian filter found here: + http://en.wikipedia.org/wiki/Canny_edge_detector +*/ +void gaussian_noise_reduce(struct image * img_in, struct image * img_out) +{ + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y; + w = img_in->width; + h = img_in->height; + img_out->width = w; + img_out->height = h; + max_x = w - 2; + max_y = w * (h - 2); + for (y = w * 2; y < max_y; y += w) { + for (x = 2; x < max_x; x++) { + img_out->pixel_data[x + y] = (2 * img_in->pixel_data[x + y - 2 - w - w] + + 4 * img_in->pixel_data[x + y - 1 - w - w] + + 5 * img_in->pixel_data[x + y - w - w] + + 4 * img_in->pixel_data[x + y + 1 - w - w] + + 2 * img_in->pixel_data[x + y + 2 - w - w] + + 4 * img_in->pixel_data[x + y - 2 - w] + + 9 * img_in->pixel_data[x + y - 1 - w] + + 12 * img_in->pixel_data[x + y - w] + + 9 * img_in->pixel_data[x + y + 1 - w] + + 4 * img_in->pixel_data[x + y + 2 - w] + + 5 * img_in->pixel_data[x + y - 2] + + 12 * img_in->pixel_data[x + y - 1] + + 15 * img_in->pixel_data[x + y] + + 12 * img_in->pixel_data[x + y + 1] + + 5 * img_in->pixel_data[x + y + 2] + + 4 * img_in->pixel_data[x + y - 2 + w] + + 9 * img_in->pixel_data[x + y - 1 + w] + + 12 * img_in->pixel_data[x + y + w] + + 9 * img_in->pixel_data[x + y + 1 + w] + + 4 * img_in->pixel_data[x + y + 2 + w] + + 2 * img_in->pixel_data[x + y - 2 + w + w] + + 4 * img_in->pixel_data[x + y - 1 + w + w] + + 5 * img_in->pixel_data[x + y + w + w] + + 4 * img_in->pixel_data[x + y + 1 + w + w] + + 2 * img_in->pixel_data[x + y + 2 + w + w]) / 159; + } + } + #ifdef CLOCK + printf("Gaussian noise reduction - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +/* + CALC_GRADIENT_SOBEL + calculates the result of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle +*/ +/*void calc_gradient_sobel(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) {*/ +void calc_gradient_sobel(struct image * img_in, int g[], int dir[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y, g_x, g_y; + float g_div; + w = img_in->width; + h = img_in->height; + max_x = w - 3; + max_y = w * (h - 3); + for (y = w * 3; y < max_y; y += w) { + for (x = 3; x < max_x; x++) { + g_x = (2 * img_in->pixel_data[x + y + 1] + + img_in->pixel_data[x + y - w + 1] + + img_in->pixel_data[x + y + w + 1] + - 2 * img_in->pixel_data[x + y - 1] + - img_in->pixel_data[x + y - w - 1] + - img_in->pixel_data[x + y + w - 1]); + g_y = 2 * img_in->pixel_data[x + y - w] + + img_in->pixel_data[x + y - w + 1] + + img_in->pixel_data[x + y - w - 1] + - 2 * img_in->pixel_data[x + y + w] + - img_in->pixel_data[x + y + w + 1] + - img_in->pixel_data[x + y + w - 1]; + #ifndef ABS_APPROX + g[x + y] = sqrt(g_x * g_x + g_y * g_y); + #endif + #ifdef ABS_APPROX + g[x + y] = abs(g_x[x + y]) + abs(g_y[x + y]); + #endif + if (g_x == 0) { + dir[x + y] = 2; + } else { + g_div = g_y / (float) g_x; + /* the following commented-out code is slightly faster than the code that follows, but is a slightly worse approximation for determining the edge direction angle + if (g_div < 0) { + if (g_div < -1) { + dir[n] = 0; + } else { + dir[n] = 1; + } + } else { + if (g_div > 1) { + dir[n] = 0; + } else { + dir[n] = 3; + } + } + */ + if (g_div < 0) { + if (g_div < -2.41421356237) { + dir[x + y] = 0; + } else { + if (g_div < -0.414213562373) { + dir[x + y] = 1; + } else { + dir[x + y] = 2; + } + } + } else { + if (g_div > 2.41421356237) { + dir[x + y] = 0; + } else { + if (g_div > 0.414213562373) { + dir[x + y] = 3; + } else { + dir[x + y] = 2; + } + } + } + } + } + + } + #ifdef CLOCK + printf("Calculate gradient Sobel - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +/* + CALC_GRADIENT_SCHARR + calculates the result of the Scharr version of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle + may have better rotational symmetry +*/ +void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y, n; + float g_div; + w = img_in->width; + h = img_in->height; + max_x = w - 1; + max_y = w * (h - 1); + n = 0; + for (y = w; y < max_y; y += w) { + for (x = 1; x < max_x; x++) { + g_x[n] = (10 * img_in->pixel_data[x + y + 1] + + 3 * img_in->pixel_data[x + y - w + 1] + + 3 * img_in->pixel_data[x + y + w + 1] + - 10 * img_in->pixel_data[x + y - 1] + - 3 * img_in->pixel_data[x + y - w - 1] + - 3 * img_in->pixel_data[x + y + w - 1]); + g_y[n] = 10 * img_in->pixel_data[x + y - w] + + 3 * img_in->pixel_data[x + y - w + 1] + + 3 * img_in->pixel_data[x + y - w - 1] + - 10 * img_in->pixel_data[x + y + w] + - 3 * img_in->pixel_data[x + y + w + 1] + - 3 * img_in->pixel_data[x + y + w - 1]; + #ifndef ABS_APPROX + g[n] = sqrt(g_x[n] * g_x[n] + g_y[n] * g_y[n]); + #endif + #ifdef ABS_APPROX + g[n] = abs(g_x[n]) + abs(g_y[n]); + #endif + if (g_x[n] == 0) { + dir[n] = 2; + } else { + g_div = g_y[n] / (float) g_x[n]; + if (g_div < 0) { + if (g_div < -2.41421356237) { + dir[n] = 0; + } else { + if (g_div < -0.414213562373) { + dir[n] = 1; + } else { + dir[n] = 2; + } + } + } else { + if (g_div > 2.41421356237) { + dir[n] = 0; + } else { + if (g_div > 0.414213562373) { + dir[n] = 3; + } else { + dir[n] = 2; + } + } + } + } + n++; + } + } + #ifdef CLOCK + printf("Calculate gradient Scharr - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} +/* + NON_MAX_SUPPRESSION + using the estimates of the Gx and Gy image gradients and the edge direction angle determines whether the magnitude of the gradient assumes a local maximum in the gradient direction + if the rounded edge direction angle is 0 degrees, checks the north and south directions + if the rounded edge direction angle is 45 degrees, checks the northwest and southeast directions + if the rounded edge direction angle is 90 degrees, checks the east and west directions + if the rounded edge direction angle is 135 degrees, checks the northeast and southwest directions +*/ +void non_max_suppression(struct image * img, int g[], int dir[]) {//float theta[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y; + w = img->width; + h = img->height; + max_x = w; + max_y = w * h; + for (y = 0; y < max_y; y += w) { + for (x = 0; x < max_x; x++) { + switch (dir[x + y]) { + case 0: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w] && g[x + y] > g[x + y + w]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 1: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w - 1] && g[x + y] > g[x + y + w + 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 2: + if (g[x + y] > g[x + y - 1] && g[x + y] > g[x + y + 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 3: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w + 1] && g[x + y] > g[x + y + w - 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + default: + printf("ERROR - direction outside range 0 to 3"); + break; + } + } + } + #ifdef CLOCK + printf("Non-maximum suppression - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} +/* + ESTIMATE_THRESHOLD + estimates hysteresis threshold, assuming that the top X% (as defined by the HIGH_THRESHOLD_PERCENTAGE) of edge pixels with the greatest intesity are true edges + and that the low threshold is equal to the quantity of the high threshold plus the total number of 0s at the low end of the histogram divided by 2 +*/ +void estimate_threshold(struct image * img, int * high, int * low) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int i, max, pixels, high_cutoff; + int histogram[256]; + max = img->width * img->height; + for (i = 0; i < 256; i++) { + histogram[i] = 0; + } + for (i = 0; i < max; i++) { + histogram[img->pixel_data[i]]++; + } + pixels = (max - histogram[0]) * HIGH_THRESHOLD_PERCENTAGE; + high_cutoff = 0; + i = 255; + while (high_cutoff < pixels) { + high_cutoff += histogram[i]; + i--; + } + *high = i; + i = 1; + while (histogram[i] == 0) { + i++; + } + *low = (*high + i) * LOW_THRESHOLD_PERCENTAGE; + #ifdef PRINT_HISTOGRAM + for (i = 0; i < 256; i++) { + printf("i %d count %d\n", i, histogram[i]); + } + #endif + + #ifdef CLOCK + printf("Estimate threshold - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void hysteresis (int high, int low, struct image * img_in, struct image * img_out) +{ + #ifdef CLOCK + clock_t start = clock(); + #endif + int x, y, n, max; + max = img_in->width * img_in->height; + for (n = 0; n < max; n++) { + img_out->pixel_data[n] = 0x00; + } + for (y=0; y < img_out->height; y++) { + for (x=0; x < img_out->width; x++) { + if (img_in->pixel_data[y * img_out->width + x] >= high) { + trace (x, y, low, img_in, img_out); + } + } + } + #ifdef CLOCK + printf("Hysteresis - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +int trace(int x, int y, int low, struct image * img_in, struct image * img_out) +{ + int y_off, x_off;//, flag; + if (img_out->pixel_data[y * img_out->width + x] == 0) + { + img_out->pixel_data[y * img_out->width + x] = 0xFF; + for (y_off = -1; y_off <=1; y_off++) + { + for(x_off = -1; x_off <= 1; x_off++) + { + if (!(y == 0 && x_off == 0) && range(img_in, x + x_off, y + y_off) && img_in->pixel_data[(y + y_off) * img_out->width + x + x_off] >= low) { + if (trace(x + x_off, y + y_off, low, img_in, img_out)) + { + return(1); + } + } + } + } + return(1); + } + return(0); +} + +int range(struct image * img, int x, int y) +{ + if ((x < 0) || (x >= img->width)) { + return(0); + } + if ((y < 0) || (y >= img->height)) { + return(0); + } + return(1); +} + +void dilate_1d_h(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); + } + } +} + +void dilate_1d_v(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); + } + } +} + +void erode_1d_h(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); + } + } +} + +void erode_1d_v(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); + } + } +} + +void erode(struct image * img_in, struct image * img_scratch, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + erode_1d_h(img_in, img_scratch); + erode_1d_v(img_scratch, img_out); + #ifdef CLOCK + printf("Erosion - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + dilate_1d_h(img_in, img_scratch); + dilate_1d_v(img_scratch, img_out); + #ifdef CLOCK + printf("Dilation - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + erode(img_in, img_scratch, img_scratch2); + dilate(img_scratch2, img_scratch, img_out); + #ifdef CLOCK + printf("Morphological opening - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + dilate(img_in, img_scratch, img_scratch2); + erode(img_scratch2, img_scratch, img_out); + #ifdef CLOCK + printf("Morphological closing - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +} diff --git a/LegacyComponents/fast-edge.h b/LegacyComponents/fast-edge.h new file mode 100755 index 0000000000..4ebee6c545 --- /dev/null +++ b/LegacyComponents/fast-edge.h @@ -0,0 +1,61 @@ +/* + FAST-EDGE + Copyright (c) 2009 Benjamin C. Haynor + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _FASTEDGE +#define _FASTEDGE + +namespace ocr{ +//#define WIDTH 640 // uncomment to define width for situations where width is always known +//#define HEIGHT 480 // uncomment to define heigh for situations where height is always known + +//#define CLOCK // uncomment to show running times of image processing functions (in seconds) +//#define ABS_APPROX // uncomment to use the absolute value approximation of sqrt(Gx ^ 2 + Gy ^2) +//#define PRINT_HISTOGRAM // uncomment to print the histogram used to estimate the threshold + struct image { + int width; + int height; + unsigned char * pixel_data; + }; + +void canny_edge_detect(struct image * img_in, struct image * img_out); +void gaussian_noise_reduce(struct image * img_in, struct image * img_out); +void calc_gradient_sobel(struct image * img_in, int g[], int dir[]); +void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]); +void non_max_suppression(struct image * img, int g[], int dir[]); +void estimate_threshold(struct image * img, int * high, int * low); +void hysteresis (int high, int low, struct image * img_in, struct image * img_out); +int trace (int x, int y, int low, struct image * img_in, struct image * img_out); +int range (struct image * img, int x, int y); +void dilate_1d_h(struct image * img, struct image * img_out); +void dilate_1d_v(struct image * img, struct image * img_out); +void erode_1d_h(struct image * img, struct image * img_out); +void erode_1d_v(struct image * img, struct image * img_out); +void erode(struct image * img_in, struct image * img_scratch, struct image * img_out); +void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out); +void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out); +void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out); +} +#endif diff --git a/LegacyComponents/genann.c b/LegacyComponents/genann.c new file mode 100755 index 0000000000..11c80bbaf9 --- /dev/null +++ b/LegacyComponents/genann.c @@ -0,0 +1,357 @@ +/* + * GENANN - Minimal C Artificial Neural Network + * + * Copyright (c) 2015, 2016 Lewis Van Winkle + * + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#include "genann.h" + +#include +#include +#include +#include +#include + +#define LOOKUP_SIZE 4096 + +double genann_act_sigmoid(double a) { + if (a < -45.0) return 0; + if (a > 45.0) return 1; + return 1.0 / (1 + exp(-a)); +} + + +double genann_act_sigmoid_cached(double a) { + /* If you're optimizing for memory usage, just + * delete this entire function and replace references + * of genann_act_sigmoid_cached to genann_act_sigmoid + */ + const double min = -15.0; + const double max = 15.0; + static double interval; + static int initialized = 0; + static double lookup[LOOKUP_SIZE]; + + /* Calculate entire lookup table on first run. */ + if (!initialized) { + interval = (max - min) / LOOKUP_SIZE; + int i; + for (i = 0; i < LOOKUP_SIZE; ++i) { + lookup[i] = genann_act_sigmoid(min + interval * i); + } + /* This is down here to make this thread safe. */ + initialized = 1; + } + + int i; + i = (int)((a-min)/interval+0.5); + if (i <= 0) return lookup[0]; + if (i >= LOOKUP_SIZE) return lookup[LOOKUP_SIZE-1]; + return lookup[i]; +} + + +double genann_act_threshold(double a) { + return a > 0; +} + + +double genann_act_linear(double a) { + return a; +} + + +genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs) { + if (hidden_layers < 0) return 0; + if (inputs < 1) return 0; + if (outputs < 1) return 0; + if (hidden_layers > 0 && hidden < 1) return 0; + + + const int hidden_weights = hidden_layers ? (inputs+1) * hidden + (hidden_layers-1) * (hidden+1) * hidden : 0; + const int output_weights = (hidden_layers ? (hidden+1) : (inputs+1)) * outputs; + const int total_weights = (hidden_weights + output_weights); + + const int total_neurons = (inputs + hidden * hidden_layers + outputs); + + /* Allocate extra size for weights, outputs, and deltas. */ + const int size = sizeof(genann) + sizeof(double) * (total_weights + total_neurons + (total_neurons - inputs)); + genann *ret = malloc(size); + if (!ret) return 0; + + ret->inputs = inputs; + ret->hidden_layers = hidden_layers; + ret->hidden = hidden; + ret->outputs = outputs; + + ret->total_weights = total_weights; + ret->total_neurons = total_neurons; + + /* Set pointers. */ + ret->weight = (double*)((char*)ret + sizeof(genann)); + ret->output = ret->weight + ret->total_weights; + ret->delta = ret->output + ret->total_neurons; + + genann_randomize(ret); + + ret->activation_hidden = genann_act_sigmoid_cached; + ret->activation_output = genann_act_sigmoid_cached; + + return ret; +} + + +genann *genann_read(FILE *in) { + int inputs, hidden_layers, hidden, outputs; + fscanf(in, "%d %d %d %d", &inputs, &hidden_layers, &hidden, &outputs); + + genann *ann = genann_init(inputs, hidden_layers, hidden, outputs); + + int i; + for (i = 0; i < ann->total_weights; ++i) { + fscanf(in, " %le", ann->weight + i); + } + + return ann; +} + + +genann *genann_copy(genann const *ann) { + const int size = sizeof(genann) + sizeof(double) * (ann->total_weights + ann->total_neurons + (ann->total_neurons - ann->inputs)); + genann *ret = malloc(size); + if (!ret) return 0; + + memcpy(ret, ann, size); + + /* Set pointers. */ + ret->weight = (double*)((char*)ret + sizeof(genann)); + ret->output = ret->weight + ret->total_weights; + ret->delta = ret->output + ret->total_neurons; + + return ret; +} + + +void genann_randomize(genann *ann) { + int i; + for (i = 0; i < ann->total_weights; ++i) { + double r = GENANN_RANDOM(); + /* Sets weights from -0.5 to 0.5. */ + ann->weight[i] = r - 0.5; + } +} + + +void genann_free(genann *ann) { + /* The weight, output, and delta pointers go to the same buffer. */ + free(ann); +} + + +double const *genann_run(genann const *ann, double const *inputs) { + double const *w = ann->weight; + double *o = ann->output + ann->inputs; + double const *i = ann->output; + + /* Copy the inputs to the scratch area, where we also store each neuron's + * output, for consistency. This way the first layer isn't a special case. */ + memcpy(ann->output, inputs, sizeof(double) * ann->inputs); + + int h, j, k; + + const genann_actfun act = ann->activation_hidden; + const genann_actfun acto = ann->activation_output; + + /* Figure hidden layers, if any. */ + for (h = 0; h < ann->hidden_layers; ++h) { + for (j = 0; j < ann->hidden; ++j) { + double sum = 0; + for (k = 0; k < (h == 0 ? ann->inputs : ann->hidden) + 1; ++k) { + if (k == 0) { + sum += *w++ * -1.0; + } else { + sum += *w++ * i[k-1]; + } + } + *o++ = act(sum); + } + + + i += (h == 0 ? ann->inputs : ann->hidden); + } + + double const *ret = o; + + /* Figure output layer. */ + for (j = 0; j < ann->outputs; ++j) { + double sum = 0; + for (k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k) { + if (k == 0) { + sum += *w++ * -1.0; + } else { + sum += *w++ * i[k-1]; + } + } + *o++ = acto(sum); + } + + /* Sanity check that we used all weights and wrote all outputs. */ + assert(w - ann->weight == ann->total_weights); + assert(o - ann->output == ann->total_neurons); + + return ret; +} + + +void genann_train(genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate) { + /* To begin with, we must run the network forward. */ + genann_run(ann, inputs); + + int h, j, k; + + /* First set the output layer deltas. */ + { + double const *o = ann->output + ann->inputs + ann->hidden * ann->hidden_layers; /* First output. */ + double *d = ann->delta + ann->hidden * ann->hidden_layers; /* First delta. */ + double const *t = desired_outputs; /* First desired output. */ + + + /* Set output layer deltas. */ + if (ann->activation_output == genann_act_linear) { + for (j = 0; j < ann->outputs; ++j) { + *d++ = *t++ - *o++; + } + } else { + for (j = 0; j < ann->outputs; ++j) { + *d++ = (*t - *o) * *o * (1.0 - *o); + ++o; ++t; + } + } + } + + + /* Set hidden layer deltas, start on last layer and work backwards. */ + /* Note that loop is skipped in the case of hidden_layers == 0. */ + for (h = ann->hidden_layers - 1; h >= 0; --h) { + + /* Find first output and delta in this layer. */ + double const *o = ann->output + ann->inputs + (h * ann->hidden); + double *d = ann->delta + (h * ann->hidden); + + /* Find first delta in following layer (which may be hidden or output). */ + double const * const dd = ann->delta + ((h+1) * ann->hidden); + + /* Find first weight in following layer (which may be hidden or output). */ + double const * const ww = ann->weight + ((ann->inputs+1) * ann->hidden) + ((ann->hidden+1) * ann->hidden * (h)); + + for (j = 0; j < ann->hidden; ++j) { + + double delta = 0; + + for (k = 0; k < (h == ann->hidden_layers-1 ? ann->outputs : ann->hidden); ++k) { + const double forward_delta = dd[k]; + const int windex = k * (ann->hidden + 1) + (j + 1); + const double forward_weight = ww[windex]; + delta += forward_delta * forward_weight; + } + + *d = *o * (1.0-*o) * delta; + ++d; ++o; + } + } + + + /* Train the outputs. */ + { + /* Find first output delta. */ + double const *d = ann->delta + ann->hidden * ann->hidden_layers; /* First output delta. */ + + /* Find first weight to first output delta. */ + double *w = ann->weight + (ann->hidden_layers + ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * ann->hidden * (ann->hidden_layers-1)) + : (0)); + + /* Find first output in previous layer. */ + double const * const i = ann->output + (ann->hidden_layers + ? (ann->inputs + (ann->hidden) * (ann->hidden_layers-1)) + : 0); + + /* Set output layer weights. */ + for (j = 0; j < ann->outputs; ++j) { + for (k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; ++k) { + if (k == 0) { + *w++ += *d * learning_rate * -1.0; + } else { + *w++ += *d * learning_rate * i[k-1]; + } + } + + ++d; + } + + assert(w - ann->weight == ann->total_weights); + } + + + /* Train the hidden layers. */ + for (h = ann->hidden_layers - 1; h >= 0; --h) { + + /* Find first delta in this layer. */ + double const *d = ann->delta + (h * ann->hidden); + + /* Find first input to this layer. */ + double const *i = ann->output + (h + ? (ann->inputs + ann->hidden * (h-1)) + : 0); + + /* Find first weight to this layer. */ + double *w = ann->weight + (h + ? ((ann->inputs+1) * ann->hidden + (ann->hidden+1) * (ann->hidden) * (h-1)) + : 0); + + + for (j = 0; j < ann->hidden; ++j) { + for (k = 0; k < (h == 0 ? ann->inputs : ann->hidden) + 1; ++k) { + if (k == 0) { + *w++ += *d * learning_rate * -1.0; + } else { + *w++ += *d * learning_rate * i[k-1]; + } + } + ++d; + } + + } + +} + + +void genann_write(genann const *ann, FILE *out) { + fprintf(out, "%d %d %d %d", ann->inputs, ann->hidden_layers, ann->hidden, ann->outputs); + + int i; + for (i = 0; i < ann->total_weights; ++i) { + fprintf(out, " %.20e", ann->weight[i]); + } +} + + diff --git a/LegacyComponents/genann.h b/LegacyComponents/genann.h new file mode 100755 index 0000000000..3678ab60b4 --- /dev/null +++ b/LegacyComponents/genann.h @@ -0,0 +1,110 @@ +/* + * GENANN - Minimal C Artificial Neural Network + * + * Copyright (c) 2015, 2016 Lewis Van Winkle + * + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + + +#ifndef __GENANN_H__ +#define __GENANN_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GENANN_RANDOM +/* We use the following for uniform random numbers between 0 and 1. + * If you have a better function, redefine this macro. */ +#define GENANN_RANDOM() (((double)rand())/RAND_MAX) +#endif + + +typedef double (*genann_actfun)(double a); + + +typedef struct genann { + /* How many inputs, outputs, and hidden neurons. */ + int inputs, hidden_layers, hidden, outputs; + + /* Which activation function to use for hidden neurons. Default: gennann_act_sigmoid_cached*/ + genann_actfun activation_hidden; + + /* Which activation function to use for output. Default: gennann_act_sigmoid_cached*/ + genann_actfun activation_output; + + /* Total number of weights, and size of weights buffer. */ + int total_weights; + + /* Total number of neurons + inputs and size of output buffer. */ + int total_neurons; + + /* All weights (total_weights long). */ + double *weight; + + /* Stores input array and output of each neuron (total_neurons long). */ + double *output; + + /* Stores delta of each hidden and output neuron (total_neurons - inputs long). */ + double *delta; + +} genann; + + + +/* Creates and returns a new ann. */ +genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs); + +/* Creates ANN from file saved with genann_write. */ +genann *genann_read(FILE *in); + +/* Sets weights randomly. Called by init. */ +void genann_randomize(genann *ann); + +/* Returns a new copy of ann. */ +genann *genann_copy(genann const *ann); + +/* Frees the memory used by an ann. */ +void genann_free(genann *ann); + +/* Runs the feedforward algorithm to calculate the ann's output. */ +double const *genann_run(genann const *ann, double const *inputs); + +/* Does a single backprop update. */ +void genann_train(genann const *ann, double const *inputs, double const *desired_outputs, double learning_rate); + +/* Saves the ann. */ +void genann_write(genann const *ann, FILE *out); + + +double genann_act_sigmoid(double a); +double genann_act_sigmoid_cached(double a); +double genann_act_threshold(double a); +double genann_act_linear(double a); + + +#ifdef __cplusplus +} +#endif + +#endif /*__GENANN_H__*/ diff --git a/LegacyComponents/ocr.h b/LegacyComponents/ocr.h new file mode 100644 index 0000000000..5808d04eee --- /dev/null +++ b/LegacyComponents/ocr.h @@ -0,0 +1,11 @@ +#import + +#ifdef __cplusplus +extern "C" { +#endif + +NSString *recognizeMRZ(UIImage *input, CGRect *boundingRect); + +#ifdef __cplusplus +} +#endif diff --git a/LegacyComponents/ocr.mm b/LegacyComponents/ocr.mm new file mode 100755 index 0000000000..4469b5f8c7 --- /dev/null +++ b/LegacyComponents/ocr.mm @@ -0,0 +1,645 @@ +#import "ocr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "fast-edge.h" +#include "genann.h" + +#import "LegacyComponentsInternal.h" + +#ifndef max +#define max(a, b) (a>b ? a : b) +#define min(a, b) (a detectLines(struct image* img, int threshold){ + // The size of the neighbourhood in which to search for other local maxima + const int neighbourhoodSize = 4; + + // How many discrete values of theta shall we check? + const int maxTheta = 180; + + // Using maxTheta, work out the step + const double thetaStep = M_PI / maxTheta; + + int width=img->width; + int height=img->height; + // Calculate the maximum height the hough array needs to have + int houghHeight = (int) (sqrt(2.0) * max(height, width)) / 2; + + // Double the height of the hough array to cope with negative r values + int doubleHeight = 2 * houghHeight; + + // Create the hough array + int* houghArray = new int[maxTheta*doubleHeight]; + memset(houghArray, 0, sizeof(int)*maxTheta*doubleHeight); + + // Find edge points and vote in array + int centerX = width / 2; + int centerY = height / 2; + + // Count how many points there are + int numPoints = 0; + + // cache the values of sin and cos for faster processing + double* sinCache = new double[maxTheta]; + double* cosCache = new double[maxTheta]; + for (int t = 0; t < maxTheta; t++) { + double realTheta = t * thetaStep; + sinCache[t] = sin(realTheta); + cosCache[t] = cos(realTheta); + } + + // Now find edge points and update the hough array + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + // Find non-black pixels + if ((img->pixel_data[y*width+x] & 0x000000ff) != 0) { + // Go through each value of theta + for (int t = 0; t < maxTheta; t++) { + + //Work out the r values for each theta step + int r = (int) (((x - centerX) * cosCache[t]) + ((y - centerY) * sinCache[t])); + + // this copes with negative values of r + r += houghHeight; + + if (r < 0 || r >= doubleHeight) continue; + + // Increment the hough array + houghArray[t*doubleHeight+r]++; + + } + + numPoints++; + } + } + } + + // Initialise the vector of lines that we'll return + std::vector lines; + + // Only proceed if the hough array is not empty + if (numPoints == 0){ + delete[] houghArray; + delete[] sinCache; + delete[] cosCache; + return lines; + } + + // Search for local peaks above threshold to draw + for (int t = 0; t < maxTheta; t++) { + //loop: + for (int r = neighbourhoodSize; r < doubleHeight - neighbourhoodSize; r++) { + + // Only consider points above threshold + if (houghArray[t*doubleHeight+r] > threshold) { + + int peak = houghArray[t*doubleHeight+r]; + + // Check that this peak is indeed the local maxima + for (int dx = -neighbourhoodSize; dx <= neighbourhoodSize; dx++) { + for (int dy = -neighbourhoodSize; dy <= neighbourhoodSize; dy++) { + int dt = t + dx; + int dr = r + dy; + if (dt < 0) dt = dt + maxTheta; + else if (dt >= maxTheta) dt = dt - maxTheta; + if (houghArray[dt*doubleHeight+dr] > peak) { + // found a bigger point nearby, skip + goto loop; + } + } + } + + // calculate the true value of theta + double theta = t * thetaStep; + + // add the line to the vector + line l={theta, (double)r-houghHeight}; + lines.push_back(l); + + } + loop: + continue; + } + } + + delete[] houghArray; + delete[] sinCache; + delete[] cosCache; + return lines; + } +} + +NSDictionary *findCornerPoints(UIImage *bitmap) { + CGImageRef imageRef = bitmap.CGImage; + uint32_t width = (uint32_t)CGImageGetWidth(imageRef); + uint32_t height = (uint32_t)CGImageGetHeight(imageRef); + + struct ocr::image imgIn, imgOut; + imgIn.width = imgOut.width = width; + imgIn.height = imgOut.height = height; + imgIn.pixel_data = (uint8_t *)malloc(width * height); + imgOut.pixel_data = (uint8_t *)calloc(width * height, 1); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + uint8_t *bitmapPixels = (uint8_t *)calloc(height * width * 4, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(bitmapPixels, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + + CGContextRelease(context); + + for(unsigned int y=0;y> 8) + ((px & 0xFF0000) >> 16))/3); + } + } + + ocr::canny_edge_detect(&imgIn, &imgOut); + + std::vector lines=ocr::detectLines(&imgOut, 100); + for(NSUInteger i = 0; i < width * height; i++) { + imgOut.pixel_data[i]/=2; + } + std::vector> parallelGroups; + for(int i = 0; i < 36; i++) { + parallelGroups.emplace_back(); + } + ocr::line *left = NULL; + ocr::line *right = NULL; + ocr::line *top = NULL; + ocr::line *bottom = NULL; + for(std::vector::iterator l = lines.begin(); l!= lines.end();) { + // remove lines at irrelevant angles + if(!(l->theta>M_PI*0.4 && l->thetathetatheta>M_PI*0.9)){ + l=lines.erase(l); + continue; + } + // remove vertical lines close to the middle of the image + if((l->thetatheta>M_PI*0.9) && (uint32_t)abs((int)l->r) < height / 4){ + l=lines.erase(l); + continue; + } + // find the leftmost and rightmost lines + if(l->thetatheta>M_PI*0.9){ + double rk=l->theta<0.5 ? 1.0 : -1.0; + if(!left || left->r>l->r*rk){ + left=&*l; + } + if(!right || right->rr*rk){ + right=&*l; + } + } + // group parallel-ish lines with 5-degree increments + parallelGroups[(uint32_t)floor(l->theta / M_PI * 36)].push_back(*l); + ++l; + } + + // the text on the page tends to produce a lot of parallel lines - so we assume the top & bottom edges of the page + // are topmost & bottommost lines in the largest group of horizontal lines + std::vector& largestParallelGroup=parallelGroups[0]; + for(std::vector>::iterator group=parallelGroups.begin();group!=parallelGroups.end();++group){ + if(largestParallelGroup.size()size()) + largestParallelGroup=*group; + } + + for(std::vector::iterator l=largestParallelGroup.begin();l!=largestParallelGroup.end();++l){ + // If the image is horizontal, we assume it's just the data page or an ID card so we're going for the topmost line. + // If it's vertical, it likely contains both the data page and the page adjacent to it so we're going for the line that is closest to the center of the image. + // Nobody in their right mind is going to be taking vertical pictures of ID cards, right? + if(width>height){ + if(!top || top->r>l->r){ + top=&*l; + } + }else{ + if(!top || fabs(l->r)r)){ + top=&*l; + } + } + if(!bottom || bottom->rr){ + bottom=&*l; + } + } + + bool foundTopLeft=false, foundTopRight=false, foundBottomLeft=false, foundBottomRight=false; + NSMutableDictionary *points = [[NSMutableDictionary alloc] init]; + + if(top && bottom && left && right){ + //LOGI("bottom theta %f", bottom->theta); + if(bottom->theta>1.65 || bottom->theta<1.55){ + //LOGD("left: %f, right: %f\n", left->r, right->r); + double centerX=width/2.0; + double centerY=height/2.0; + double ltsin=sin(left->theta); + double ltcos=cos(left->theta); + double rtsin=sin(right->theta); + double rtcos=cos(right->theta); + double ttsin=sin(top->theta); + double ttcos=cos(top->theta); + double btsin=sin(bottom->theta); + double btcos=cos(bottom->theta); + for (int y = -((int)height)/4; y < (int)height; y++) { + int lx = (int) (((left->r - ((y - centerY) * ltsin)) / ltcos) + centerX); + int ty = (int) (((top->r - ((lx - centerX) * ttcos)) / ttsin) + centerY); + if(ty==y){ + points[@0]=@(lx); + points[@1]=@(y); + foundTopLeft=true; + if(foundTopRight) + break; + } + int rx = (int) (((right->r - ((y - centerY) * rtsin)) / rtcos) + centerX); + ty = (int) (((top->r - ((rx - centerX) * ttcos)) / ttsin) + centerY); + if(ty==y){ + points[@2]=@(rx); + points[@3]=@(y); + foundTopRight=true; + if(foundTopLeft) + break; + } + } + for (int y = height+height/3; y>=0; y--) { + int lx = (int) (((left->r - ((y - centerY) * ltsin)) / ltcos) + centerX); + int by = (int) (((bottom->r - ((lx - centerX) * btcos)) / btsin) + centerY); + if(by==y){ + points[@4]=@(lx); + points[@5]=@(y); + foundBottomLeft=true; + if(foundBottomRight) + break; + } + int rx = (int) (((right->r - ((y - centerY) * rtsin)) / rtcos) + centerX); + by = (int) (((bottom->r - ((rx - centerX) * btcos)) / btsin) + centerY); + if(by==y){ + points[@6]=@(rx); + points[@7]=@(y); + foundBottomRight=true; + if(foundBottomLeft) + break; + } + } + }else{ + //LOGD("No perspective correction needed"); + } + } + + free(imgIn.pixel_data); + free(imgOut.pixel_data); + + if(foundTopLeft && foundTopRight && foundBottomLeft && foundBottomRight) { + return points; + } + return nil; +} + +NSArray *binarizeAndFindCharacters(UIImage *inBmp, UIImage **outBinaryImage) { + CGImageRef imageRef = inBmp.CGImage; + uint32_t width = (uint32_t)CGImageGetWidth(imageRef); + uint32_t height = (uint32_t)CGImageGetHeight(imageRef); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + uint8_t *bitmapPixels = (uint8_t *)calloc(height * width * 4, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(bitmapPixels, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaNoneSkipFirst); + CGColorSpaceRelease(colorSpace); + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + + CGContextRelease(context); + + uint8_t *outPixels = (uint8_t *)malloc(width * height * 1); + + uint32_t histogram[256]={0}; + uint32_t intensitySum=0; + for(unsigned int y=0;y best_sigma) { + best_sigma = sigma; + threshold = thresh; + } + } + + for(unsigned int y=0;y0 + && outPixels[width * y + x +1]==0 + && outPixels[width * y + x -1]==0 + && outPixels[width * (y + 1) + x]==0 + && outPixels[width * (y - 1) + x]==0){ + outPixels[width * y + x]=0; + } + } + } + + if (outBinaryImage != nil) + { + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + CGContextRef context = CGBitmapContextCreate(outPixels, width, height, 8, width, colorSpace, kCGImageAlphaNone); + CGColorSpaceRelease(colorSpace); + + CGImageRef imgRef = CGBitmapContextCreateImage(context); + UIImage *img = [UIImage imageWithCGImage:imgRef]; + CGImageRelease(imgRef); + CGContextRelease(context); + + *outBinaryImage = img; + } + // search from the bottom up for continuous areas of mostly empty pixels + unsigned int consecutiveEmptyRows=0; + std::vector> emptyAreaYs; + for(unsigned int y=height-1;y>=height/2;y--){ + unsigned int consecutiveEmptyPixels=0; + unsigned int maxEmptyPixels=0; + for(unsigned int x=0;xwidth/10*8){ + consecutiveEmptyRows++; + }else if(consecutiveEmptyRows>0){ + emptyAreaYs.emplace_back(y, y+consecutiveEmptyRows); + consecutiveEmptyRows=0; + } + } + + NSMutableArray *result = [[NSMutableArray alloc] init]; + // using the areas found above, do the same thing but horizontally and between them in an attempt to ultimately find the bounds of the MRZ characters + for(std::vector>::iterator p=emptyAreaYs.begin();p!=emptyAreaYs.end();++p){ + std::vector>::iterator next=std::next(p); + if(next!=emptyAreaYs.end()){ + unsigned int lineHeight=p->first-next->second; + // An MRZ line can't really be this thin so this probably isn't one + if(lineHeight<10) + continue; + unsigned int consecutiveEmptyCols=0; + std::vector> emptyAreaXs; + for(unsigned int x=0;xsecond;yfirst;y++){ + if(outPixels[width * y + x]==0){ + consecutiveEmptyPixels++; + }else{ + maxEmptyPixels=max(maxEmptyPixels, consecutiveEmptyPixels); + consecutiveEmptyPixels=0; + if(y>p->first-3) + bottomFilledPixels++; + } + } + maxEmptyPixels=max(maxEmptyPixels, consecutiveEmptyPixels); + if(lineHeight-maxEmptyPixels0){ + emptyAreaXs.emplace_back(x-consecutiveEmptyCols, x); + consecutiveEmptyCols=0; + } + } + if(consecutiveEmptyCols>0){ + emptyAreaXs.emplace_back(width-consecutiveEmptyCols, width); + } + if(emptyAreaXs.size()>30){ + bool foundLeftPadding=false; + NSMutableArray *rects = [[NSMutableArray alloc] init]; + for(std::vector>::iterator h=emptyAreaXs.begin();h!=emptyAreaXs.end();++h){ + std::vector>::iterator nextH=std::next(h); + if(!foundLeftPadding && h->second-h->first>width/35){ + foundLeftPadding=true; + }else if(foundLeftPadding && h->second-h->first>width/30){ + if(rects.count>=30){ + break; + }else{ + // restart the search because now we've (hopefully) found the real padding + [rects removeAllObjects]; + } + } + if(nextH!=emptyAreaXs.end() && foundLeftPadding){ + unsigned int top=next->second; + unsigned int bottom=p->first; + // move the top and bottom edges towards each other as part of normalization + for(unsigned int y=top;ysecond; xfirst; x++){ + if(outPixels[width * y + x]!=0){ + top=y; + found=true; + break; + } + } + if(found) + break; + } + for(unsigned int y=bottom;y>top;y--){ + bool found=false; + for(unsigned int x=h->second; xfirst; x++){ + if(outPixels[width * y + x]!=0){ + bottom=y; + found=true; + break; + } + } + if(found) + break; + } + if(bottom-topsecond, top, nextH->first - h->second, bottom - top); + [rects addObject:[NSValue valueWithCGRect:rect]]; + } + } + } + [result addObject:rects]; + if((rects.count>=44 && result.count == 2) || (rects.count>=30 && result.count==3)){ + break; + } + } + } + } + + free(outPixels); + + if(result.count == 0) + return NULL; + + return result; +} + +NSString *performRecognition(UIImage *bitmap, int numRows, int numCols) +{ + NSString *filePath = TGComponentsPathForResource(@"ocr_nn", @"bin"); + NSData *nnData = [NSData dataWithContentsOfFile:filePath]; + + struct genann* ann=genann_init(150, 1, 90, 37); + memcpy(ann->weight, nnData.bytes, sizeof(double)*ann->total_weights); + + NSMutableString *res = [[NSMutableString alloc] init]; + const char* alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890<"; + + CGImageRef imageRef = bitmap.CGImage; + uint32_t width = (uint32_t)CGImageGetWidth(imageRef); + uint32_t height = (uint32_t)CGImageGetHeight(imageRef); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + uint8_t *bitmapPixels = (uint8_t *)calloc(height * width * 1, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 1; + NSUInteger bytesPerRow = bytesPerPixel * width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(bitmapPixels, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaNone); + CGColorSpaceRelease(colorSpace); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + CGContextRelease(context); + + double nnInput[150]; + for(int row=0;row(col*10); + unsigned int offY=static_cast(row*15); + for(unsigned int y=0;y<15;y++){ + for(unsigned int x=0;x<10;x++){ + nnInput[y*10+x]=(double)bitmapPixels[bytesPerRow * (offY+y) + offX + x]/255.0; + } + } + const double* nnOut=genann_run(ann, nnInput); + unsigned int bestIndex=0; + for(unsigned int i=0;i<37;i++){ + if(nnOut[i]>nnOut[bestIndex]) + bestIndex=i; + } + + [res appendString:[NSString stringWithFormat:@"%c", alphabet[bestIndex]]]; + } + if(row!=numRows-1) + [res appendString:@"\n"]; + } + genann_free(ann); + return res; +} + +UIImage *normalizeImage(UIImage *image) +{ + if (image.imageOrientation == UIImageOrientationUp) return image; + + UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); + [image drawInRect:(CGRect){0, 0, image.size}]; + UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return normalizedImage; +} + +NSString *recognizeMRZ(UIImage *input, CGRect *outBoundingRect) +{ + input = normalizeImage(input); + + UIImage *binaryImage; + NSArray *charRects = binarizeAndFindCharacters(input, &binaryImage); + if (charRects.count == 0) + return nil; + + uint32_t width = 10 * (int)[charRects.firstObject count]; + uint32_t height = 15 * (int)charRects.count; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width, colorSpace, kCGImageAlphaNone); + CGColorSpaceRelease(colorSpace); + + int x, y = 0; + for (NSArray *line in charRects) + { + x = 0; + for (NSValue *v in line) + { + CGRect rect = v.CGRectValue; + CGRect dest = CGRectMake(x * 10, y * 15, 10, 15); + + CGImageRef charImage = CGImageCreateWithImageInRect(binaryImage.CGImage, rect); + CGContextDrawImage(context, dest, charImage); + CGImageRelease(charImage); + + x++; + } + y++; + } + + CGImageRef charsImageRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); + + UIImage *charsImage = [UIImage imageWithCGImage:charsImageRef]; + CGImageRelease(charsImageRef); + + NSString *result = performRecognition(charsImage, (int)charRects.count, (int)[charRects.firstObject count]); + if (result != nil && outBoundingRect != NULL) + { + CGRect firstRect = [[charRects.firstObject firstObject] CGRectValue]; + firstRect.origin.y = input.size.height - firstRect.origin.y; + CGRect lastRect = [[charRects.lastObject lastObject] CGRectValue]; + lastRect.origin.y = input.size.height - lastRect.origin.y; + CGRect boundingRect = CGRectMake(CGRectGetMinX(firstRect), CGRectGetMinY(firstRect), CGRectGetMaxX(lastRect) - CGRectGetMinX(firstRect), CGRectGetMaxY(lastRect) - CGRectGetMinY(firstRect)); + *outBoundingRect = boundingRect; + } + return result; +}