From bb21568d17557d272ab1ad4f16b0379c4ee03fa6 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Wed, 3 Feb 2021 16:07:03 -0800 Subject: [PATCH] Fix encoding empty fields and flesh out tests --- Package.swift | 6 +- Sources/Osiris/MultipartFormEncoder.swift | 16 ++-- .../MultipartFormEncoderTests.swift | 86 ++++++++++++++++-- Tests/OsirisTests/Resources/lorem.txt | 9 ++ Tests/OsirisTests/Resources/notbad.jpg | Bin 0 -> 22680 bytes 5 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 Tests/OsirisTests/Resources/lorem.txt create mode 100644 Tests/OsirisTests/Resources/notbad.jpg diff --git a/Package.swift b/Package.swift index f96fe67..6cd2d46 100644 --- a/Package.swift +++ b/Package.swift @@ -23,6 +23,10 @@ let package = Package( dependencies: []), .testTarget( name: "OsirisTests", - dependencies: ["Osiris"]), + dependencies: ["Osiris"], + resources: [ + .copy("Resources/lorem.txt"), + .copy("Resources/notbad.jpg"), + ]), ] ) diff --git a/Sources/Osiris/MultipartFormEncoder.swift b/Sources/Osiris/MultipartFormEncoder.swift index f832a32..caf9e5b 100644 --- a/Sources/Osiris/MultipartFormEncoder.swift +++ b/Sources/Osiris/MultipartFormEncoder.swift @@ -39,8 +39,8 @@ extension MultipartFormEncoder { let contentLength: Int64 } - struct Part { - enum Content { + struct Part: Equatable { + enum Content: Equatable { case text(String) case binaryData(Data, type: String, filename: String) case binaryFile(URL, size: Int64, type: String, filename: String) @@ -72,7 +72,6 @@ final class MultipartFormEncoder { case invalidFile(URL) case invalidOutputFile(URL) case streamError - case emptyData case tooMuchDataForMemory } @@ -184,13 +183,12 @@ final class MultipartFormEncoder { } private func encode(data: Data, to stream: OutputStream) throws { - guard !data.isEmpty else { - throw Error.emptyData - } + guard !data.isEmpty else { return } + try data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in let uint8Bytes = bytes.baseAddress!.bindMemory(to: UInt8.self, capacity: bytes.count) let written = stream.write(uint8Bytes, maxLength: bytes.count) - if written < 0 { + if written != bytes.count { throw Error.streamError } } @@ -216,11 +214,11 @@ final class MultipartFormEncoder { while inStream.hasBytesAvailable { let bytesRead = inStream.read(buffer, maxLength: bufferSize) guard bytesRead > 0 else { - throw Error.streamError + break } let bytesWritten = stream.write(buffer, maxLength: bytesRead) - if bytesWritten < 0 { + if bytesWritten != bytesRead { throw Error.streamError } } diff --git a/Tests/OsirisTests/MultipartFormEncoderTests.swift b/Tests/OsirisTests/MultipartFormEncoderTests.swift index 25a6ab5..4f7bf67 100644 --- a/Tests/OsirisTests/MultipartFormEncoderTests.swift +++ b/Tests/OsirisTests/MultipartFormEncoderTests.swift @@ -9,10 +9,10 @@ @testable import Osiris import XCTest -func AssertStringDataEqual(_ expression1: @autoclosure () throws -> Data, _ expression2: @autoclosure () throws -> String, _ message: @autoclosure () -> String? = nil, file: StaticString = #filePath, line: UInt = #line) { - let data1 = try! expression1() +func AssertStringDataEqual(_ expression1: @autoclosure () throws -> Data, _ expression2: @autoclosure () throws -> String, _ message: @autoclosure () -> String? = nil, file: StaticString = #filePath, line: UInt = #line) throws { + let data1 = try expression1() let string1 = String(bytes: data1, encoding: .utf8)! - let string2 = try! expression2() + let string2 = try expression2() let message = message() ?? "\"\(string1)\" is not equal to \"\(string2)\"" XCTAssertEqual(data1, Data(string2.utf8), message, file: file, line: line) } @@ -28,14 +28,40 @@ class MultipartFormEncoderTests: XCTestCase { subject = nil } + func testConstructTextPart() { + let part = MultipartFormEncoder.Part.text("value", name: "name") + XCTAssertEqual(part, MultipartFormEncoder.Part(name: "name", content: .text("value"))) + } + + func testConstructDataPart() { + let data = Data("value".utf8) + let part = MultipartFormEncoder.Part.data(data, name: "name", type: "text/plain", filename: "something.txt") + let expected = MultipartFormEncoder.Part(name: "name", content: .binaryData(data, type: "text/plain", filename: "something.txt")) + XCTAssertEqual(part, expected) + } + + func testConstructBinaryFilePart() throws { + let url = Bundle.module.url(forResource: "notbad", withExtension: "jpg")! + let part = try MultipartFormEncoder.Part.file(url, name: "name", type: "image/jpeg") + let expected = MultipartFormEncoder.Part(name: "name", content: .binaryFile(url, size: 22_680, type: "image/jpeg", filename: "notbad.jpg")) + XCTAssertEqual(part, expected) + } + + func testConstructInvalidFilePart() { + let url = Bundle.module.url(forResource: "notbad", withExtension: "jpg")! + .appendingPathComponent("busted") + XCTAssertThrowsError(try MultipartFormEncoder.Part.file(url, name: "name", type: "image/jpeg")) + } + func testEncodeNothing() throws { let body = try subject.encodeData(parts: []) XCTAssertEqual(body.contentType, "multipart/form-data; boundary=\"SuperAwesomeBoundary\"") - AssertStringDataEqual(body.data, "--SuperAwesomeBoundary--") + XCTAssertEqual(body.contentLength, 24) + try AssertStringDataEqual(body.data, "--SuperAwesomeBoundary--") } func testEncodeText() throws { - AssertStringDataEqual( + try AssertStringDataEqual( try subject.encodeData(parts: [.text("Tina", name: "name")]).data, [ "--SuperAwesomeBoundary", @@ -47,9 +73,22 @@ class MultipartFormEncoderTests: XCTestCase { ) } + func testEncodeEmptyText() throws { + try AssertStringDataEqual( + try subject.encodeData(parts: [.text("", name: "name")]).data, + [ + "--SuperAwesomeBoundary", + "Content-Disposition: form-data; name=\"name\"", + "", + "", + "--SuperAwesomeBoundary--", + ].joined(separator: "\r\n") + ) + } + func testEncodeData() throws { let data = Data("phony video data".utf8) - AssertStringDataEqual( + try AssertStringDataEqual( try subject.encodeData(parts: [ .data(data, name: "video", type: "video/mp4", filename: "LiesSex&VideoTape.mp4"), ]).data, @@ -60,20 +99,42 @@ class MultipartFormEncoderTests: XCTestCase { "Content-Length: 16", "", "phony video data", - "--SuperAwesomeBoundary--" + "--SuperAwesomeBoundary--", ].joined(separator: "\r\n") ) } + func testEncodeFile() throws { + let url = Bundle.module.url(forResource: "lorem", withExtension: "txt")! + let body = try subject.encodeFile(parts: [ + .file(url, name: "lorem", type: "text/plain"), + ]) + XCTAssertEqual(body.contentType, "multipart/form-data; boundary=SuperAwesomeBoundary") + XCTAssertEqual(body.contentLength, 3586) + XCTAssertEqual(try FileManager.default.attributesOfItem(atPath: body.url.path)[.size] as! UInt64, 3586) + XCTAssertEqual(try String(contentsOf: body.url), [ + "--SuperAwesomeBoundary", + "Content-Disposition: form-data; name=\"lorem\"; filename=\"lorem.txt\"", + "Content-Type: text/plain", + "Content-Length: 3418", + "", + try! String(contentsOf: url), + + "--SuperAwesomeBoundary--", + ].joined(separator: "\r\n")) + } + func testEncodeEverything() throws { let imageData = Data("phony image data".utf8) let videoData = Data("phony video data".utf8) - AssertStringDataEqual( + let url = Bundle.module.url(forResource: "lorem", withExtension: "txt")! + try AssertStringDataEqual( try subject.encodeData(parts: [ .text("Queso", name: "name"), .data(imageData, name: "image", type: "image/jpeg", filename: "feltcute.jpg"), .text("top of the bbq", name: "spot"), .data(videoData, name: "video", type: "video/mp4", filename: "LiesSex&VideoTape.mp4"), + .file(url, name: "lorem", type: "text/plain"), ]).data, [ "--SuperAwesomeBoundary", @@ -100,7 +161,14 @@ class MultipartFormEncoderTests: XCTestCase { "", "phony video data", - "--SuperAwesomeBoundary--" + "--SuperAwesomeBoundary", + "Content-Disposition: form-data; name=\"lorem\"; filename=\"lorem.txt\"", + "Content-Type: text/plain", + "Content-Length: 3418", + "", + try! String(contentsOf: url), + + "--SuperAwesomeBoundary--", ].joined(separator: "\r\n") ) } diff --git a/Tests/OsirisTests/Resources/lorem.txt b/Tests/OsirisTests/Resources/lorem.txt new file mode 100644 index 0000000..5751953 --- /dev/null +++ b/Tests/OsirisTests/Resources/lorem.txt @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vulputate massa eget turpis auctor, bibendum tincidunt purus tempus. Aliquam id risus quis est porttitor mollis a vitae mauris. Nam magna odio, egestas ut viverra vitae, pretium vel sem. Fusce imperdiet enim non orci placerat, quis eleifend nunc malesuada. Suspendisse scelerisque lacinia volutpat. Duis efficitur ut dolor at iaculis. Donec consequat eros nec augue pellentesque, ut dapibus mauris pharetra. Maecenas eget pharetra justo. Proin bibendum a lectus sed rutrum. In faucibus dolor commodo, ornare lacus ac, vulputate quam. + +Nam mollis tortor et elit aliquam suscipit. Maecenas eget erat et justo vehicula scelerisque ac quis erat. Nullam placerat felis sit amet nibh ullamcorper, at volutpat est congue. Fusce in pharetra eros. Phasellus laoreet arcu a libero imperdiet commodo. Suspendisse facilisis, libero in pellentesque aliquam, enim libero pulvinar nibh, ac aliquam nibh ante nec velit. Proin condimentum elit ex, ut dictum tellus volutpat ac. Nunc fermentum nisl id est gravida, nec pellentesque libero blandit. Etiam vitae pellentesque eros. Pellentesque consequat urna ligula, id imperdiet lorem efficitur non. Cras at placerat nulla, vel luctus odio. Phasellus a posuere velit. Ut in magna in augue sagittis elementum ac vitae turpis. Quisque ornare velit nec cursus dignissim. + +In hac habitasse platea dictumst. Morbi lectus leo, rhoncus non scelerisque imperdiet, porttitor non neque. In vehicula posuere convallis. Nullam eu varius nisi. Ut euismod egestas mi. Phasellus eget urna egestas, suscipit ex sed, ultricies leo. Suspendisse consequat in massa quis ullamcorper. Sed eleifend nulla tellus, vitae posuere augue vulputate ac. Curabitur iaculis vulputate dui ornare laoreet. + +Maecenas ac eros cursus, ultricies diam vitae, sollicitudin metus. Suspendisse quis mattis ex. Ut vel ullamcorper sapien. Sed in mauris sed ante imperdiet consectetur vitae sed dui. Aliquam in tempus mi. Phasellus vitae augue volutpat, sodales augue sit amet, pellentesque ante. Integer nec lacinia nunc. Morbi magna eros, placerat vitae eros sed, varius imperdiet sem. Vestibulum ultrices finibus ultrices. Cras condimentum vestibulum nisi mattis posuere. Sed sed nisl nec odio posuere ullamcorper sit amet sed quam. Ut id suscipit turpis. Aliquam et sem quis ligula bibendum aliquam. Praesent efficitur vel nibh in commodo. Donec porta, nunc at aliquam consectetur, ante diam eleifend ante, at elementum est neque in ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Donec tincidunt massa urna, in ultrices arcu vulputate in. Quisque placerat nisi ipsum, eget aliquet enim dictum vel. Ut sed nulla odio. Etiam in vulputate dui. Nunc elementum sodales neque, quis tristique massa feugiat eget. Donec ultrices erat facilisis ipsum volutpat tincidunt. Etiam efficitur luctus ipsum eu convallis. Ut elementum, nisl id aliquet ultrices, nunc quam gravida lectus, non ullamcorper elit justo non lorem. Sed tincidunt efficitur lectus ac molestie. Sed vel tellus eu tortor finibus fermentum a elementum mi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus suscipit sed tortor tempor congue. Proin molestie bibendum metus. Nam ullamcorper, nunc placerat commodo egestas, ante odio faucibus nisi, vel vulputate augue nulla at ex. Duis scelerisque interdum felis, sed fringilla urna. Mauris lacinia felis gravida egestas dictum. diff --git a/Tests/OsirisTests/Resources/notbad.jpg b/Tests/OsirisTests/Resources/notbad.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dfb50dbb515ad6dc1bbdecdd78ef95f8a26cce2f GIT binary patch literal 22680 zcmeFZby!s2*Eo8HZlt?QrKC$36e$rwIz<>lgrP@}8Z1x{P*f05Qbbz18^NHvQ>056 zVqj+O8T{n;eSdL(_ql)E=eZlsK5MVN&faV1S!=I#1~-bE1WF#b{B*bK-q@?8JWE514)Krv|RBZGNw2WNr zJltIDoSeLZ5+c0(Vgj6;XBEzgU67KMmE{ppQdN|`C?O*&jV}a3PEJlmNySP{%__~u z$tV4PJK&lCIx-*t@FRrq0R(gqLOKYp4d4N}B?fy5-%)=A2mv7xF$pOdIRzz1P<{q% zF(DxV5g{=#5!i4@2$&BL(Gk=0NnIjgxM5Gq@69NE|3x~Pz~$m*CZoP>K^X_12jmpY zEUawoLc$_v&z+Z*lUGnwQr5Vlsim!>d)3&))Xdz%(hBD2T9xXwW2pI_wn3t%Y4O$FRdNzCoyWg!P#2JM2 zJB~zuZ&8!%KXJ*>&RL23!4q#`akAZru*U@Tl3*BrKTEtg$;@nQJNZT60i&cqN)uJm zQH-8y&_Yqn&lGvL5ca+bvFHQdSXE$iV+04}69Yl15m?p$z1CrJPnu{~1TT;5fwx#; z$B39Bnqth8;t(-Ho#yJUO8wRaZAff+k#vqN6bFR+#U4^8N&aF=2-;3)8u@Z1g}dTe z>ipv*X{dlYcgTF>*m$=NJCf?=SH@YF^#lEC>rr{d6SwB_cMLbSv01{YxmLPxU$#X~ z1b5m2_Z}qG);z2Z9yE)j(?oi74i{dwX-*L|*x~v}xLzP>R57&o9e%m{l2Cn{gU+Zw zNkQ*vACo={?Y$}X@_H{k7<#8o|08|>#>FGIi5?OPHS#MsXG>V!&kbTs5F^U6VaO{{AHn$gDd478X3VzjF9Ook#wLI?vV6?goLN zEn2%}<7!EJ-Z;+GBj=;p5LTY!W%KesYVX3zRd_Om_T9Cz{2DoxuEq7><23SJ2sdB~ zPyrW+0rArj3En-vYSFXbMS}x~Cf4KVgG3#923>xo(UN3@slMFz^ttGp(=k>y?D@?C z2edaNrx8Q~XVYlU^e&WE9Ih?U(%tdx@N~)Me<+(xk@Z6RS7FTYLeG8oALqChr>1Fz zr^kwqD#n4C?T9ucO~BaCDBbGkHdhp689sD$d|cP=9kEvD)?L5>QR5vCP%E-S+c|NZ zB;l*N-D43SA;~~b{B1=>5g*JXf3x5+H7e%?)IxC{nGShzBpmo zL@(J$N)!}1yu%aWx5c>pa>eQ`zurA<(i#P4(qcx)&P?QD?e{7njd7bMKr%Ia(n@De%~s3(wQ6)m z?~6e6-GvGgeJ?D3>9+Y}ev4b%$x{~|i^S+(UCDHNx+~7op-|IZC5tg`vGUt*P>*xYw~)C{1hPkxMp`rQ_Q!mq?P|G2UTAB7lF}U!e#t4S(J@d9x*H3p-By4 zN%j=Z=KsnjNWvkYsJLM!XFzUwQ`51cBGAOs1)ILP9@^``vmU(m@TiwWB4HMz7_fdv zuzYChkzHN&3sYnOhRIaj6mZ;@=@A?WQfJy+hkI^xvUq9BYUkGcFjF|;zo2~~$kyDf z@&*nN8(y!$a)2LMn_`e%|Vzzo*=l_X{zVbYepr4593OY2EB=NR*LFb1HpYO+; zy{J|??$k}ALh@Lt9<*+qvDReJ6ByMmW^^ni76ep%M0-M64%sJ~&{XWhnQr^Bba%+q zdf@HLgU=qbEqj0Go>EKW(vA{GP)oM<%(F?T4HQg#C3_(J;&U#`Yr~>=G9vpYlRiN| z%sRpqG;`OUpJ8IZT}W_lPOn{>w8AV**$V%zP{NQNC?7m2hz@K&1mUH#%M`pc}mX4~V134m}i1yP6H3wo6v&D?6;uc50drKbt@W@^n4T6|%!G_`G z!nFfq5*t5E%}ZamxC=m)<<$~N!gv$f@|@|D=Z&{1of4^t8+S+g8eqp}n(;*HzfGA@f@b`e_- z|0zxBlHQJ_*h5lk|BTi!!gAz2C`OJbwl^W^Sutd3G!O7w1x;Yv`p$IWyZ2?4o?oi4 z<>u`b3hbH7@n$SN_8;XIzEV%#VN0JNs)lDjpt<}US+7p+IalwZlfv;1+NPio_vWPb z4MK@sXAwGMgUsXI=cMbXcM3?D>*}r-eiuCcqr?L{32yhd7kH0Rgyq6=63!J5DkkF4~ippxs36)_X{CySvEyK_;{YC(FkvP z`#t!zDCyzsUA-w(>vsDq4sfKupsJXky~7;=lYXi{aP2zj=+^XN!p!0wU_F>`$3VYk z*rC@`j!snnbN;U!?I|2^@dpuYa_<3p#3A!o`BCyIu|t~ zq$gmY&Fc`>+Ha@tDb!N@>!b?3Kvaw-rZVU+IT8P`+Q85|H;`WLwg%L0By_lWbm52Mf(4P+j5pqWC%X3)I1mie-RFK?3d#+j0Km%44|S~TFTeTJ z(|hq-2>qo>=>5;WR@_g5b?>=`9Tlc7LKW@KLX&`Vuo@cKM(oq|G6_^;zKVi$7 zd@3nBC1$Jpg~v(zb>n4y%Mx(6PckWw(b;gkC}@1yUSye!5#nOIJsdl4E3YSh{oA>D z?^ZaZci+h)?KpCwV_deXAXq{ElbqhM_~oe$uED5S5icRfbsZzE*?U~-n~-J0$yP7B zuP^LiaEEMOo~tk3oR4<^2nCaX-BeZ;x4XtR0(5TPf>xRd7&)15Pi0f}!@@!c@5(Lj zoeDQ##8ghnS}v|O|IPi#m+!Lnjg*B+M4`x~nvs0Jqq2VfszK-4fR~P90_Ec*M}kVr zKAbd^o(nPRq}ExtE5@y43Y}|SCKfvW)TOQ7+vlXH_I2~T2MPbl`tun|m+=DTHeuw5 zpGx9pMnC)hCbgP5y_GIemVDKZmLI-l_n!y(UNKsYvq3uB>F11%7q@y=N*8lSDLcZ| z@*&`CHIVz6IJd@XulSVqD-m6e%X9Sk81S8r?lL}RPA`<-(AUq33AGk#FDHaCh$Z&m1L<~QE?!bO6;}#HKPO=-AC`Bi82fB6 z9fzd+BI_+4truy+&hZ(1^v3j_CHz!b6}~#H5_QkW!qM={vJWfw>|hy$cxb2~KWH;M zQjqhf77$trai)exggWeIBjMYq& z5^6!=>0i*e)v?$vs&^e#o3!2!)!9w(q6iWL(!Hh$D$oExrE|^I z!;|0)0Kh%`e2la&^McA7FKIVG0FZ-PvlIZ>!+gE;jkQeh8l@`Zu$BM^4?_w&Nzw?UZE!3_@+;B|9G zACN&1X2ruUzhSXcn%}TG9(II#ID#~Ip1mC5j(E5YgrD3+IDs(XH4qNJ>+0kW!h;|z z=#GH9g76^-Gs2zheF1=o3Xk`5g53t;3m{D8V`8KM!WY5g1uB<+zz+X_{hR_oJ^?_( z)9ap(tIKUa-m|cCyi!U^io7~b{_akGe&UAqFgJT2M_vt2xR<@hJplOKX8cnCJ-%;w z!A_Q!Qj(V!mzDs#|F7^r&HRh%e-7Zs_BX}ujngrMFn9iq`}^6yah@3fpojwd=K0?^ zheQCVd<+1r(*S_>5dc*5{G&b0`1x|%&(BL)QqtewU&7T1CV}tJzrz2L;4jMm zG4PN2B=Gh9)pxvCoSf|u?tZ-ZPK9~8dm?;zeZA~qPQ2p(If?&|AN&Vf|KLN+$jRBs z$H@b%$_$)kt{yJnaC~j|CX*B!DwLi~!+A zCqTqN2@u+(fH{!A#_bxp8Gv7T<~&Qk=RF96`KR%}B7#^jN#N`1!iz_17@6?G5I%SD zFt{f05ivjk&;pDA8^8?+0A~SlKpId0Q~-5A6SxW(049JXa1($5w*ffd1NZ~Mz{CMI=omeIyGcDDb?IjZ~CW zmDGULp45*toHUU%pR|#*pLCJ*kc^UyhfJDGi_D75gX{rWJXtPT16e=W64^2N8FC?V zWpV>@NAi2*vE=W`Ysh=Z7s-z)=qN-eR4Gg-?oiyPctcT0@tIKv_iC(Xm8R6(#F%4()Q4I%Ow^k zmZvO*Ed4A8tQ@TBtWK=V0Ve z;ec^Oa+Gk4a}sc#=QQFB;>_gi)Mue*z0YQx z9Xv;JPVt=Exp(J!&l8-NJAeCp^7$T70#SKUSJ8K(ePYC7N@5;j8Db;iRN|M!?~3P% z&q**#T$6YpQ7N%0DJW?r`9iWo5_>`Jg8PN{7bc|WrLIankgAdTC4F8RCY>xjC_^oy zC4-cylG%|Jm35L$ksXy|kkglokZX~{$ScVE$QR15C&<5jz*mZmlf<%8No zGobV80_u+HAJmsFox61BQvRjQ%NH*DTrR(SprNc0qS17P@QU`8$Sd8N^qQucNt)AI z{8}zrd0N}ra@s-KO*+IndO9z3Ms&G!VY)fG+gBB?hFopaqtY|hOVXRacK({rwHkc_ z{j2&f^(U_jUH7MXO@{ONHQbxf>9mWjCcE-8J`zDu7 zVofGY&zd4kTg+(9Y|L`Z4$QBZ$C=MsNLqwge79t`bhWIqBDFHJ`e22!*0g?Yy=Wt6 z6K*qXD`Fd9`}HRKP4}DicC>c(cBQw7Z<*i9y>()5V4q=+a?o{n>#z;efF;1z9Mv6P zIj%UVI>k9HIjcIyIWN1Yy1aB*xedJ?e|z0k!!^lu=Z?;u)H?@m25#AIICpdRA~+fR z7QEVn-owqK&6C?Rz_ZUw)GOR;)?3;8wfDA7vK@ldGGwar}vfuwF0w(h=O22Ex~-j_k-s`E`_9rLP8xvTaW_CN65u6 z?XZvcDek-7?|N|ILEMAAhvpA!9&tSid$jOa`*B`4O}J0^NQ6p6>Jy?Tu1~t3$~;Yc zij8!P{1$Z~Dn9Du8SL4&XsPHo(YP4rnC@8l*p%la&pnCYuaeKc6vpIa7KJ4N#@O7*)IZ~wc$RXOrI+hg zv{rgotydSfE;2Ufl87!Tox+%eI@nC%e~zGDJ=9!}pI5 zA`jUPKOQL_b)e1BOUM2vq$h7M=P)%`J?tb7j>F+L_+$-ld-{3$-uCq3m65mrC|=gp zClbO72Eduq_#e?}p7JT;ljK6~d z9Vm)}>WGJ*)1P^G7>@(#PighNF1tH;db;CFradj!*fju8e%aLzkH^#guFuKmx;@;< zMAHJVF5t;e`BT9YK=777JpaG>qyGng23~k+4%8F?dhh}?y!<@=EEeR!!RN1pk&CbX zUkME#4~@SQ9)5o(t~t2-{*`mx#qaK42~D{BmA?{TGyg97GR)1zz~K(4GW{vC{n0ao zc$SPDd3hZ@5f18}v-sp`(Dw28FSyI@|CR2tkE4mHho6>!k^8Aa^XK!I-5q)VJG_yv zyWc6^Fu+~i948p|`vHo(PB1@DpDXr$_V^Z@_M)MSq3@rAhKKQ#;Qe$!uQ@sYF6KX~ zH34VUe^;$B%>6gs&*u- zKGkLL)6Lz}!{yI%@YC|2>C1ke@Zag*Y#4!>{vT<)nndF3?h14AHFLj)pWKjt)kA!m z2cq%22I*-+_sT!YCUx=gM0ouRMdAq_CAfP0v1X_=jq#-$oZ`STsO=Gcp4v_xPCoX2 z_`?kVFuCXT=dMWcn;4J5=jg&u)wln*+fRt_asS753RaBo{C{KgeO>;!<$|4N@9t+} z@A5Ay8kiIK9C)Up>#Jj;f9=mLgzQiBzlh0id-?=ka(8w4v!`f(H(uvYG{^@j=#_v! z5&%K+yG}lS{|&j>pXh&+Q#!b4fJX>E|L9!0-{oJ{{wo7~1i0?$f$wW_KTj|4Mds`D z&xu3f4o;;1f~9is^aCfp2L{0ucP>$3_CM0ua!PWdoqPBIT;n`q(VnYK}p9%OGQP?#LB?H#K6ExM|B$hxcyH2XAGgHq@<>yK0`xuhLMJb zh7ljpF#djo?tiBM+^K)db0Ccn!vCj#%M`d)DR6BND9Gi%y<7-EZ;wB{T)@r$-`q41TLCXSjdnSSyt{igFyze*pq4j>kGwM1UMe_ZyuZScol zR{7_lMUu+9x>t1t@z^pFe=n~{F|AcKwD+`$2D5!c}AjwqG&%Gx{_>&&j*nhG|F zYi3@BEVUQmW&usmv%>+#aNTqBD>#5H)pkW~`r{JK9BL4>LDamhk$8z!MrU28T0Jy% zGEb8?)HkD@4oboSR(a5mGo?8$$fq<}wm~&W<7^?!7{cQ{(rKts+)LFu19d$r-V6Of zX2+92!|u%7F>17HXuM}}c<&X48s)!cpoRV#=@aNQ@txOJqkY0nP=|S>2vgOy*SITY zwf2+y#Sl6|t0(WH-?6?D6BpTB>uz3gW2!X{P>NcH=Gs|Zz?SZXyDY?BA5Jwm|FG+B z7-jNF&6LV&=>c>uBa z;@D7^%PSZt=jTL>0S~JI1I%L#ifa1YKQ)9c+Dq%a_-x%qQnvthu3~kKuYkj1s-nIS zeePVJE=}^vm#l<^(TSW-q~O#!qd2Fs+Wxx2BTK)=$vFS=Tc!dIba1f1L%k%vZ5{%9q^5KYj zJeY^r;>#~=Z-`q`+;9^;);ot1^Nrng*FYbkEN3jyl%H|~iq0oId*%@OG{|Z{&aAN)-wBGN_P9qM0Z|NHJg46!05jX;`z|qLY_5FJgB2cN+W8EuN zLv_aYz7!vh#79JKdPvx%zgST@>bbOuVx8HG>W2QzzMA{i*{}Pli&c^M&kDq~WFHN^ zH#p$(v3uK!yXRPO($?@f)F#Ta9qT!CGkwUfXL)mDzp^Y<#Nj7$W}hEX^hp77;Lc&Zp-<&^49SD>=b}xAwGLx2f1iBOq*vgqZ zC5vjcuLiJPFQ^(R89;bc6nxNl(?L-XM7tDdruwWB!;J$1uLkcjq|G5}-galAf++VA zEWX+G!#groSKK&POQyg1O0+k87ZWS$MKx+}RrI*LuZ8BZNht2mjwPKO%spp=qiXWw z2j}+(%nb{t;-TkKqHuua-tA*kwbwXcyJ*-$O$DwX|AEqWFP|FHF~9ZxNn9!q4&Vym zPEwAcHP5NFd($!S0sw5U_gwAZdB4VE+K_s3Q}s^jAT)2vIvtTu(>U}gKaZx#@`-=1 zSV(1ApD~Ht7~>C>AaP{zYqhOIp;e5_>XyNp`KrnPXX40v*!;Lskpev}YO{!KX3z7|+UU_e%Mj&focE*R zzl+ydtzw)bCyaMC{L_|(XN*H_)n2F0;(&RK)eLNn()O5#e|UA>wpyRCx;;pSy=Mc> zVa8%}7iqj&>4$yGx->ovtKVl>8HsE*^EdjGG&!QOA3MH%fz25DcCWB>u&7^G7wMX+ zHK~AcX{L59b-s<@afQru4#BO%nb-Y8RFbvVm_lQF+bdB~LUvbItE()O%$#o|zaAjm zZ0XE@;3L61f_BSu6V0SwjecbpYf#V#y5NE@y8z}N!AAA98oXcTvy|Y_1Iafj#n${`>wFDH{;xe z+8V9C_Ka%M*y$dVbmw}z!+iYU1jn=0oymi_e)me>(vn~9h-Y0$OvVagCL|>x({7?H z@L_}nx>fs{?^1UE_jia}aNnZr^({8mBYli2^DWDS$YIN9Cr{LREq0;a{YL-9O~dbv z{OjM}^nZJIw@H-rsMXX01shtmJQ}V}DLRDy>^BO&=sZ6^6f?qthC_P%`-2+QKv&2X zD;&^oc~a*1*ZW=Y6%N1+{A(^5wGr^H((?JLO1(44woU9W&dHyI!@NsuY_`XV;ZFAw z3nCk}O__$CYBE_jLZLjOdvEs82lIPtiE9|6Rprk#D{!ak0L19}Fv}VEM_EViL}T`m zt?oS>V4H;l_FwKXO{@$I;ee~JR%r9;Ra@`s6@9B)^?_Hi!Fy%G)PUW71yn>R^_VQ~ZNp2= zM%P9KrJiOdyZ2w=DW(4L36?eMZ#$+mS^bpuRn}FZ5+~5@#ycf>s!VAU+?O*j?#=fG2r z%~qRM63cI(Vsqvu_eou5R_8CNyxR}(Fit~XW%!CWxi-fL^Zd3}m{KCk-n$&fEyIJ%cYTUGU2?tOn-zrM& zc4D2mQ;q|6{6k*2Ai5LpYH@hi#8{VX_Iz>}(BN2Jh&2ic3YW1t_@Tl#asNRq_$-0dNsNc_kkG+ea+st*7k3&)_(X{1*J!KMh ztNw@0IN)8XML2Xcv_+~|A{gCJ_-jqdny0g`)LrSWtpVMbMGV}&u%@&j4F_Z_0he-n zve06YlP5uqvju@y;qQxgE=+olz3h{w5i%e}iH@!Iw~#x%4pdpIr5V3DqurY+us-OH zjrsk3&P-{Ysw&2~A(ZLm>7pCOd$)$yPa?mWRt{DUxzim&E8A2JFp~` z%g#YC^iM7+P$Y%e41bERY4r|K9G%W13%WC5ra8u;sshbGhLxc6JzkMs`@%(v9T?In z{ZO^LY-X)r)g!P{OFRt2CQcvU{?dU`4UR1F5x?r_rToD@B|ZN3$K9uo^kgv;zEhPF zJ!|vWZga0~ukAZ~UMo&a-Sfk;@;0F_XLzo6goeZ(WWJAkRU*YbIbHn%F^7U?m-R)* z!tTaR$k%M9b%an_z9@cWxQYYbIGH0Ke{JM`hlofzKm`xq7~ovBz3+;u$>bM>YKV|> zRmguU*(gp;|AJz3mf&i9k9`+qE0?9<^Q)-g!0&_S6FGPt|OmMAExU7gB z6d8YR>u60cgH<}gU*<@73xeB}#jiE-ijJW5ijKf|=+WoLsF78;o%MyC;?y{Au4va! z7Hq5Z{rw#yl_Azhnd!LHl-%5j4JX;+;R&~gq6J~q-M%ryQqMfAdwk=!^wANh(wv(o zfq@~fEsfcnqQ69vIfb{I-~a>O5m?f}!qgtyQPe2*J;r1?L?GKZR816mn|f#_ey66@ z$s<$_o19v^z8(b1+Pwof;DPwHz_)6pOXJ$*ql(vDcbQQlD%gUj=Z#^e$f&3X z!?Npot`sYRwwnmVnqBf;W34LO+sye~=v1K^UC?(j)tCtfmri{=$Tv}a1g+9m^`X-J z{O*q);a{A}yJyN_9ujxE?%G}7GN#|^!^USXI63a5&Lh(u_ZkvXUnUwafBjZdk(8Rw zR8ldc5rvfMIfh}1Y%7r0GnJk(vzWt0Le(@~;N@!a=&}Z`qinae;))v~$n-C{*N*CYMxqs=I^U!7I4Z%S%Iwg6fqMZRL;(`iLR4?5l^O znGQ4ezGwBcboqIFYv+sg3$or1SE_(5Zg}<{+bW>#W-IupV#*(XFjEP9tZ||yXnvb# z`;B>{TLl)YW*cvbrKP?j7?))64))=v^|EmQ8=%`>rkoeX^VZvk!%S;tG4SeY`Y;Y~ zsmyNZPFMeyla*1Ur}1{}=Ffp>Ta`?w{TU~Ou`_xf^l)4fH9>2jQ%LlbT}(dN?T4$a zt614=UAs^Q{V?&uf5m4#qgE8bR)H-}wi|b^Nvfyq7lPu3QS8gPwc_Q`5tADE{!iUB z@#8$i;q6+pDj9$gmU+86DXaFgCc6v=@OAn3VQ_#xo8YVW;0cVFI`6~g-VcQ}s|DpI zeYWn9e3&{laC^AC04;80QI2t2iAP*ZNvIh9;ZV_`Sf$`r&sV*XH&fj+v56*QBO6Mp zCltQO=Ci};B{R$EThtf3rJ^gAl8j3JfZ|R>ns}T9+qfvj*UUmVtiF>|=L~OReJ74q zV|Ye3@4t?pH1v2sIy=5m9(Ez_Zsp`ieHZvLCTp9y%N`h!=X~Rf@0A~fR6V61uCg=* zsRRVT+gRjcglj!Qx?E3MT~XqmdBtaF+=`o7>DMy^2rlO1H6B}KbM5ZCG^XY?!{vn% zU^NlsJlBU+u#vTWf#5!}=GQPOqOy0Lx}-9UdkigD#u8+AltcCWw3ZiY28rQ(DHiXg zbO@x)GqQeFp8Z(T89~QgaDVpjGr}AkxxNI2r>KLB#igN#w*FBPpG-Qc^Qk7U53h>* z?pM!zF;iX|#wPdv8he^xx>bydx|EYIKCU-*v~RwNVdvggmY!#nEnk{D8U4zsO;!Ju zMzN**mG+7XbD!zw@b(Dyw_^DRZeiln9ig7(JuWBK7+KBNz1WhyhAJ^u4_|mL_YU6no__4Upq`eO$uBJxviGvvrN`;k1B-X9ys*UpYEzBV81$xnna zeE3;WbHVHFuZf4PPnoL=Yj3&w9t0t^SEaLVS}e8FwIJI0D*6}ZQObu((`HJ(I!o;& zY97JOD5O%jO;IV2+sW1%6fp>%BO->CZ_2biop|#`oxCOnSV09PKp$S45e^ z99exWhA--cH0p@nczW>D?K)lF<76?~mZM;YTir@NY#LD`tk_t_o0MxBzs69j*>h4A zi?aQ$1wYxBn#M4JKr7f>_aRj$EUV>&&9i|f9DZ@)^=TOiBURm?p5d`sk&)7q(jpR z#zto5rtji3e{nQi;{?>?{P2?Njz6gU?6Keg4>6Mh^!>pO?JU&b0Yb)=#n3%(03Lc+ zT!T@7=bfDIU3*8lCShQ-5~9b#R52A19RsH+zMp}fJhrp`@a>?|e^a=f;fz4o7Wr+J z;LfpoDdxff*L+#Kw3Aw#&b9M&BjwO`BAfc8gUAzegydmu<1R|C*PIWfvN~hBg3%Mr zK?#>6Opi#F%Qd&(+@CxMq8uMShTcQ0j)>Dst%@2Uyc%Q@xUYNcbUHq>C-YX{(THB@>6zNZjq zgasJ>7ZkkU=fTFnZ&U7R4Zp3SqyXvfI!?bICV)Wj|9=ERVnTc%0l!@ZZ4#i9A9#_G zQ<9UBQGlNqQ&3Y-P~wd%q~tWT)KoNh`1Jc>@M-*0;6D`^DcOITU;fwW-}aR-Aow5l zmH+?y;r|bRKa4rahy=f_hz!SKsn55&<>jPTR#rx5#ar6&Ux}3N>n!6IR22^_rb z&ftJ%BOEZ>fFwZ9#+xOuL!XrEUUb^w?Wmz8y)FGdp_sn*Md1$-r}r*rLxaDVJ{0tT zbo?|pVG+U7R1eC%GqyT3I2K*)cSR#=RpX0ym+IAOd>f~mMmr)oPKu?K!DOlN zt+D9MLMcy_4t+CAe~#(Ds%cNBM_=4kZdsbN&hD>kyo>`LmO2 zizqB|iw3@3fhHDpfnQCaN}E!*?{cf1Z+w?~mhjjS z6NQ}p1Rnk%PZ$aGf}r7cj3=tOZX>|!UdeZ5Q@5T=H^V&$ba`(KWzaNkm=6Fy8^e+* zkM?SpJqnzYrhiUr#-DTWiMz7ulb^3dbcRgexn}4DvYEDVc8!)kSa3uZ`4~I^-7mg3 zA4@u^Y~|XZTrDo2lRI|{8A6Rfa}De1mM6A@N8S~Fb3-$zHclp zYFa_)9j_hD74PIH@n1Zs=^8u^en$gg6>Hiumi}NlHOvrJDuAs-Hk%_o;R;yt;$yPI z-TCsRv7bY-$p*#C%*H$khC?oxsr)OB%!W&yUP^Qwd>eMJ8nO4&aY(*8&+1bX?%(;$ravVUcxPuH+W1f)-a9O__ zSnEnHa1G0rvI>>Ur+TKRoXfg+dCZ=3Cr8YrG}u6$rvka{#&g1QeRmewM$NPDa#)bB zt8o8B-$y){A$Lm08cTP4#Ig9v=iaegYXt-L-qj|U<1_HJugeS0XA5VyduBRgnQ_u= zme(m>^ggs3n>jg?y#Fg1OCbVkvz>=Q3k6o|Hv?+*AJ?hO`-@l47?rkpitf)`)Z%zR zO1Qzgq0VF1rwM-6r^yI8q{RWG4WYdUhM;km0Lw5wB|T=N`!S%>xT5E~;1l}(t{v=` zxI<*q8mbaYE^sVbv~CGjGIpr2y;#8h{YGD~oTpj=L&%RokNY+CZ2Q$ohN@v)dA}sF zlv~@Xjqb6BN)h{Zht{f%6SD6#)ca`CNp1J_a#zh2>X0L4(xIluSen~F#dK+pj@}%2^s>np z+m$q-F;Yg{YAZFlVHhG&{8QmPfolQvljrHl7EaS z6J5RWn9a>nhg#mJ?`rpvRP`AwdoRSUPy2)wwUFGUF&n!)giyUgo<+cD*hqtRv2Shc5f^RZ71WYqlq0t2wz0I3S$@z-<41<(wtl z3BppW?5OdKBC!;eJ3RYE_3WxsOD)icH7<|qSh8)tO^2q>7u;Dhj_edu5B}&`5<;@)O#f*49udZh-;q*kG-T8nYohT4yHJYji8(dabP6 z=q&{AS)KclGEcw7YbP!MoPdVV2LZ@tIjmGLJ0`!52Qk|?v9Rw}xs$P~yHCx~iUZDU z*6FJio&735VA1}h^vZ%4LcGPpL+S;FITSnuFH>#Yus@ax5SbUMf0m>dkf#^9xpw`< zK|`XSbb@^TD91CI_r>vqL?7I?x8``(K`UL)p|HrhZq^L8t297#KH_r`v9@pSnjOPM zNmqZ*%zHArRFzhaBLmr^h&D z57O;-2!U@j4(jIUCvM$TtBNkndh_vX%X8_#ULt74%)0jW8kU0gSQR{gq{50UH%@SK z3{iA(Xo|e_|1g=xFIww*PpXUiXLGX=+w0da60Nt9`wzh`Aq;JT5}tVG9Qqq!iZ0rw zua3)n2R&5ACswytBpHia`KR>Z?)>*NlWxPjMbph+GtMPm!ai1$J^{7D$Dg;8+fo>? zQ#&k=JD$pp?G5BDD>w|83|uLGC1ZWf^pUUddftYL1!0WnFYWzWur`Ln1e-(iIVkDj zL!-l)`6Ajw(N>?st21VY*2py1jXYOLf3BG^6c@8azPUpeC;W+rL}%=o7h&!ITxo6HZMe|}7<+KXDhMbL? z*)~aiI>N2cj{}@%#&=tk8(WLUDaNNYdr{UC2wR1D@jCJvUHDS$1zo=<OTe z`)k!Tu5*FgRL8d3n^f{WRf*mG^lizLb00lfv|eKAMlW(Owc6Bt_c}O`y5d;x%B{G| zmY5C?WBnE!;mWGYs%N^4J5}eutd3FTzkfj1 zOv-fPk8Eyk+}7uS9tta*FhZ@jdm_YtM9dvLgPXTunaqSfXO(rY2&F8Hd`VHdaY?AI zNa#)67Mmk1XE;Wgx08G0>mjQh3Y!csqKt3lgVq^waI*kI3mxt$br2XU)!Pofg@dOgm5( zYoy4JHKGKi69{=GV)jB7{h*e4JK2Kdo3a9Ljp}yQGmf`<&H<7^O181-Y^Ma9cH%e}}m$-7X-`&r0lV>NlJ2FBG?nmU$`pL=E~L*^}b zV&PAZBGH0{ZcDKYZ4K<;^es9Rw)j?bB5X&FIxK{)r1zsj*wT5zhBkH3<$>lK#yh@- zhlIz{Czp4IUuIN3K|x6leGP{<{K`Wj4CBVfb>qdJrM~%~NVMA|!U$PvWGaUo>x2&9 z-f#h-6l1F{6~dsec(LSL^v9~>zGP9(2bH`dIl(T6Yje=CrIrfx zLA2bHU}*Z}9p-V^9djKV04qzHzr?5(oW}S-;YFO3SQdorZJ6vo$-~(e?upKL*OFgG@MHZx!)B-SETAOEF%sx%Ob1BOr}P zjF&bY36CAe0dtM(McbtoRXckMDf|zQh1ZO#w}%t5xLj^D45Zmi*>Gr*Vwi=XbxF3L z3Kz|8b>}v5_gfRhH>!aP?`tRtvKcf~&b|V@ek2>%Xrp*&uv}}RKJExwmOJRTo^ulg zs{A!&gL|zAe`%}n@y_RZEMt~@+U26<*W5`#%V0Af)UAj+bO>1@|7eD#On$qkU7XMW zoIQ_{T6E%>YhzZ2Rr16&9d6Qj+ek}c=@v;0?OHF9>+?Z!sEc5i6&b~?8PJ$-pVI5oEVsUKA z&?c8P^RziA?+{DxG=i(b4SFA00(~N8Uom`9W+67RM!wRXs)un+8R>B6!0Sib7Mqi! z))7~j)2jDP@T`saF6e|lsZLOaXsNK=tApJNld7_7qj8aY*v!RXGdT21l_L}*Ef!p4$WK4M_#P{UjelWO7&XhjjLSinuK75)irCG9_vuM z!Q^?@lUz%V-er0<02EP00DR^9M5}2Z@KK-Hw^HOsd8YWgK;7!oTG-wN{6?I|AME>9 z{{Z|HYgCbcV_zC*HnKEYm&6EQ@HU?6`&v7lX5u|EIRWeYvvuYYIxK8^R!8mK6L@#V z{{XY!jJA)n_($TEls9jVL|U$$sOV@uhUZhZKUEdTe%zlMv>yik(LNEn@n!GWJ|FxB z@aC)WgT>b&H+gbL47zreZdX4!l+UV78jP`X1QadAH#wj4rP(ZQw%{y{pSMp7I-bBPKYZFXhC9tZJ*pe0}iG$KSLE zkNhpF`1{5G01kX%;C~Z*Qq<@1HOY&>n%9gqZ?nqVY%PVooCyV#cGfFx0t<lY(5cW_`gDxulzUSKLhyo#>ZRmwa`rT<-(!71}<5+?1PNiuyAtb%BSLRze zI+n6WQywg z3uly&2vHhJ@_g6~P%d)9y~vyj_z(8Z_{Zaqj34k)Uxq&k^gI0~TODC9Z}@FyR)Ba2@EiM2ZeuO&3oYo zfuZnE!+(qZGVnjc`&9crpQQ^MEmrTtw%x6C>$|w5XyHM`X>zwdQsEP1XpyI`d>HsO zp!@{*bz|Xw4%^&Ar&yUKX)bPLy1TueS7~OsxSm4NPjNJ^@<}X=&m+4t5={VgKw^q0 zpaP00pbs(qvwjL+e$f8_vhR&NC8XR(XW|`0Pd6G3+=mNgt6Q?%>GPjxuHk}1{^fjo z@MFOV{{RI!_}}7h6>G7{r}!sXo4`ISy3npbcGR@%3r#}e9WHT<18?FHKc5?QYY8G{ z`OZH?49XzI=P)ZwB0Wx5W3JF3|6F&xgJOO+v!M!}q#{?c5*Rv0P6q zCV15^8rM@enBZP&4FnP}nIr)EqvQVo?P1}6+2-p>((j_yd`06A5eNRn@TZ5Y&HlBl z1i*KW5JJZS7C`qiyjJlSK_scUbHO+5JMq#4T|vKQO=4Xwx%(6zJJFk5RrD}4i)Xvo zUXlLQc7L+}0PN7e1T^hST=52^zAN~h;{zSni#$&7`E97oyS7_fp(pJ6eYkc@dtgXK zC7q<2Nv4<8=mX4t1OC(~xPUPW z_KRnl0^A7+uOI!eeiM8s{i?2^@vnw7pZHk68omnNTij@O9}m1srdfEmLYd?M!{^*j z9k_*~d7166OF=4mfX3v2eS7w?{hw~WKm0UEv>j?s3V83scPp&;OT-bUm~@RwHDHEB z{{WV@vZFi~5afBVO)3JcRoCOc4u8QrV$l2m-ZJ=E@GroA3-MOHeKq%sJUeNsT=+-C zdIq!ym2oDIuUf|E?5xUOJ85DTw-Ly%x;W%$$Oo=|&p#5cz7l*__>r%8x5GXl@Xw0A zK6tv#6K+_2CVkiKXecHc48=CdFP(56=Hb9UX0U*=wD&9sKvfV)G?2qD%l`lh zJ_tkLFZe0%!hZtIeJ_YLuZRBt5ByE>C&jwkKqCIw*Y)j2(#2weQssC)^*2B?DM4xsz4ik{f)J+8Cu<6S$NyP*S5O9fn~6@H+Pfxjimc6g_KY! z24Q<8$+40kRJ=E{F67Sj_pcxPK+ygb_%l@TABnY%GsFHL(QVrAP}H>>q_woNg+^LQ zC2|!M4y2GP@|*Ve{jfZB`%nJLUNrrryfdWVXg>>l19PY8e+Cm&wUXNN#QMgmc@C8R zFlbv395&Ej$~5TY8E0m>bq(YQz&`o^0D^sf-g*!1>F`=TY8@w3@L!HJQ5C0*d=srg z_PWo6bpsf7g5nt!BoVMX;*_F^Vo7AunJ?O`ZXl6T?kAqn+1T!y9X2Hd$zJiNf5ARH7h@Ne zd{gnl;v8-A=lH$ihU3$B+xH*D&Lp>NbqdY-&@#A&&z+P2Kya=7~DkitffKn;+3$CYfk?Ff^U2c zg86SX4~G69@m2Dj%Xi|TCy4G;`iXDaX9wII*Rp9EEuV+9yL%lz(%Qz#)dX6z$K|pA!BhXg>#h1FU$X#M+&1ik_CQk>(>7O8NX+L5dO%%JJ5VBsbA~24dbs6c=N%anDslIFI&BXSiG`| zGBG9nt2#=MmQONEMvOS$$Oq~L6^HQS;y#D*bHMsvi99!{TWcB~u>#!L-N15_A@(>b Wz=Eu-11kcg0tg$F08vF00sq;L5BUoK literal 0 HcmV?d00001