From bf380fb2810f946952bf8651a68b4a27872047e7 Mon Sep 17 00:00:00 2001 From: budain Date: Sat, 23 Jul 2022 16:01:12 +0200 Subject: [PATCH] Feat: adding multipart support (#8) --- Package.swift | 7 +- README.md | 56 +++++ .../Encoder/MultipartFormDataEncoder.swift | 104 +++++++++ .../URLRequest/URLRequest+Multipart.swift | 13 ++ Sources/SimpleHTTP/HTTP/HTTPContentType.swift | 4 + Sources/SimpleHTTP/HTTP/HTTPHeader.swift | 1 + .../Request/MultipartFormData.swift | 206 ++++++++++++++++++ .../Request/Request+URLRequest.swift | 7 +- Sources/SimpleHTTP/Request/Request.swift | 13 +- .../MultipartFormDataEncoderTests.swift | 163 ++++++++++++++ .../Extensions/XCTestCase+URL.swift | 7 + .../Request/MultipartFormDataTest.swift | 68 ++++++ .../Request/RequestTests.swift | 62 ++++-- .../Ressources/Images/swift.png | Bin 0 -> 32014 bytes .../Ressources/Images/swiftUI.png | Bin 0 -> 53458 bytes .../Ressources/URL+fromBundle.swift | 41 ++++ Tests/SimpleHTTPTests/URL+fixture.swift | 12 + 17 files changed, 743 insertions(+), 21 deletions(-) create mode 100644 Sources/SimpleHTTP/Encoder/MultipartFormDataEncoder.swift create mode 100644 Sources/SimpleHTTP/Foundation/URLRequest/URLRequest+Multipart.swift create mode 100644 Sources/SimpleHTTP/Request/MultipartFormData.swift create mode 100644 Tests/SimpleHTTPTests/Encoder/MultipartFormDataEncoderTests.swift create mode 100644 Tests/SimpleHTTPTests/Extensions/XCTestCase+URL.swift create mode 100644 Tests/SimpleHTTPTests/Request/MultipartFormDataTest.swift create mode 100644 Tests/SimpleHTTPTests/Ressources/Images/swift.png create mode 100644 Tests/SimpleHTTPTests/Ressources/Images/swiftUI.png create mode 100644 Tests/SimpleHTTPTests/Ressources/URL+fromBundle.swift create mode 100644 Tests/SimpleHTTPTests/URL+fixture.swift diff --git a/Package.swift b/Package.swift index 454c2ba..02391ab 100644 --- a/Package.swift +++ b/Package.swift @@ -24,6 +24,11 @@ let package = Package( dependencies: []), .testTarget( name: "SimpleHTTPTests", - dependencies: ["SimpleHTTP"]), + dependencies: ["SimpleHTTP"], + resources: [ + .copy("Ressources/Images/swift.png"), + .copy("Ressources/Images/swiftUI.png") + ] + ), ] ) diff --git a/README.md b/README.md index a37623c..088ae05 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,62 @@ A few words about Session: - You can skip encoder and decoder if you use JSON - You can provide a custom `URLSession` instance if ever needed +## Send a body + +### Encodable + +You will build your request by sending your `body` to construct it: + +```swift +struct UserBody: Encodable {} + +extension Request { + static func login(_ body: UserBody) -> Self where Output == LoginResponse { + .post("login", body: .encodable(body)) + } +} +``` + +We defined a `login(_:)` request which will request login endpoint by sending a `UserBody` and waiting for a `LoginResponse` + +### Multipart + +You we build 2 requests: + +- send `URL` +- send a `Data` + +```swift +extension Request { + static func send(audio: URL) throws -> Self where Output == SendAudioResponse { + var multipart = MultipartFormData() + try multipart.add(url: audio, name: "define_your_name") + return .post("sendAudio", body: .multipart(multipart)) + } + + static func send(audio: Data) throws -> Self where Output == SendAudioResponse { + var multipart = MultipartFormData() + try multipart.add(data: data, name: "your_name", fileName: "your_fileName", mimeType: "right_mimeType") + return .post("sendAudio", body: .multipart(multipart)) + } +} +``` + +We defined the 2 `send(audio:)` requests which will request `sendAudio` endpoint by sending an `URL` or a `Data` and waiting for a `SendAudioResponse` + +We can add multiple `Data`/`URL` to the multipart + +```swift +extension Request { + static func send(audio: URL, image: Data) throws -> Self where Output == SendAudioImageResponse { + var multipart = MultipartFormData() + try multipart.add(url: audio, name: "define_your_name") + try multipart.add(data: image, name: "your_name", fileName: "your_fileName", mimeType: "right_mimeType") + return .post("sendAudioImage", body: .multipart(multipart)) + } +} +``` + ## Interceptor Protocol `Interceptor` enable powerful request interceptions. This include authentication, logging, request retrying, etc... diff --git a/Sources/SimpleHTTP/Encoder/MultipartFormDataEncoder.swift b/Sources/SimpleHTTP/Encoder/MultipartFormDataEncoder.swift new file mode 100644 index 0000000..6a55bee --- /dev/null +++ b/Sources/SimpleHTTP/Encoder/MultipartFormDataEncoder.swift @@ -0,0 +1,104 @@ +import Foundation + +struct MultipartFormDataEncoder { + + let boundary: String + private var bodyParts: [BodyPart] + + // + // The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more + // information, please refer to the following article: + // - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html + // + private let streamBufferSize = 1024 + + public init(body: MultipartFormData) { + self.boundary = body.boundary + self.bodyParts = body.bodyParts + } + + mutating func encode() throws -> Data { + var encoded = Data() + + if var first = bodyParts.first { + first.hasInitialBoundary = true + bodyParts[0] = first + } + + if var last = bodyParts.last { + last.hasFinalBoundary = true + bodyParts[bodyParts.count - 1] = last + } + + for bodyPart in bodyParts { + encoded.append(try encodeBodyPart(bodyPart)) + } + + return encoded + } + + private func encodeBodyPart(_ bodyPart: BodyPart) throws -> Data { + var encoded = Data() + + if bodyPart.hasInitialBoundary { + encoded.append(Boundary.data(for: .initial, boundary: boundary)) + } else { + encoded.append(Boundary.data(for: .encapsulated, boundary: boundary)) + } + + encoded.append(try encodeBodyPart(headers: bodyPart.headers)) + encoded.append(try encodeBodyPart(stream: bodyPart.stream, length: bodyPart.length)) + + if bodyPart.hasFinalBoundary { + encoded.append(Boundary.data(for: .final, boundary: boundary)) + } + + return encoded + } + + private func encodeBodyPart(headers: [Header]) throws -> Data { + let headerText = headers.map { "\($0.name.key): \($0.value)\(EncodingCharacters.crlf)" } + .joined() + + EncodingCharacters.crlf + + return Data(headerText.utf8) + } + + private func encodeBodyPart(stream: InputStream, length: Int) throws -> Data { + var encoded = Data() + + stream.open() + defer { stream.close() } + + while stream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = stream.read(&buffer, maxLength: streamBufferSize) + + if let error = stream.streamError { + throw BodyPart.Error.inputStreamReadFailed(error.localizedDescription) + } + + if bytesRead > 0 { + encoded.append(buffer, count: bytesRead) + } else { + break + } + } + + guard encoded.count == length else { + throw BodyPart.Error.unexpectedInputStreamLength(expected: length, bytesRead: encoded.count) + } + + return encoded + } + +} + +extension BodyPart { + + enum Error: Swift.Error { + case inputStreamReadFailed(String) + case unexpectedInputStreamLength(expected: Int, bytesRead: Int) + } + +} diff --git a/Sources/SimpleHTTP/Foundation/URLRequest/URLRequest+Multipart.swift b/Sources/SimpleHTTP/Foundation/URLRequest/URLRequest+Multipart.swift new file mode 100644 index 0000000..11b810b --- /dev/null +++ b/Sources/SimpleHTTP/Foundation/URLRequest/URLRequest+Multipart.swift @@ -0,0 +1,13 @@ +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +extension URLRequest { + public mutating func multipartBody(_ body: MultipartFormData) throws { + var multipartEncode = MultipartFormDataEncoder(body: body) + httpBody = try multipartEncode.encode() + setHeaders([.contentType: HTTPContentType.multipart(boundary: body.boundary).value]) + } +} diff --git a/Sources/SimpleHTTP/HTTP/HTTPContentType.swift b/Sources/SimpleHTTP/HTTP/HTTPContentType.swift index b443cf6..4d06019 100644 --- a/Sources/SimpleHTTP/HTTP/HTTPContentType.swift +++ b/Sources/SimpleHTTP/HTTP/HTTPContentType.swift @@ -15,4 +15,8 @@ public struct HTTPContentType: Hashable, ExpressibleByStringLiteral { extension HTTPContentType { public static let json: Self = "application/json" + public static let octetStream: Self = "application/octet-stream" + public static func multipart(boundary: String) -> Self { + .init(value: "multipart/form-data; boundary=\(boundary)") + } } diff --git a/Sources/SimpleHTTP/HTTP/HTTPHeader.swift b/Sources/SimpleHTTP/HTTP/HTTPHeader.swift index 608f790..8a9a63f 100644 --- a/Sources/SimpleHTTP/HTTP/HTTPHeader.swift +++ b/Sources/SimpleHTTP/HTTP/HTTPHeader.swift @@ -16,6 +16,7 @@ extension HTTPHeader { public static let accept: Self = "Accept" public static let authentication: Self = "Authentication" public static let contentType: Self = "Content-Type" + public static var contentDisposition: Self = "Content-Disposition" } @available(*, unavailable, message: "This is a reserved header. See https://developer.apple.com/documentation/foundation/nsurlrequest#1776617") diff --git a/Sources/SimpleHTTP/Request/MultipartFormData.swift b/Sources/SimpleHTTP/Request/MultipartFormData.swift new file mode 100644 index 0000000..531de97 --- /dev/null +++ b/Sources/SimpleHTTP/Request/MultipartFormData.swift @@ -0,0 +1,206 @@ +import Foundation +import UniformTypeIdentifiers + +#if os(iOS) || os(watchOS) || os(tvOS) +import MobileCoreServices +#elseif os(macOS) +import CoreServices +#endif + +struct Header: Hashable { + + let name: HTTPHeader + let value: String + +} + +enum EncodingCharacters { + static let crlf = "\r\n" +} + +enum Boundary { + + enum `Type` { + case initial + case encapsulated + case final + } + + static func random() -> String { + UUID().uuidString + } + + static func string(for type: Boundary.`Type`, boundary: String) -> String { + switch type { + case .initial: + return "--\(boundary)\(EncodingCharacters.crlf)" + case .encapsulated: + return "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)" + case .final: + return "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)" + } + } + + static func data(for type: Boundary.`Type`, boundary: String) -> Data { + let boundaryText = Self.string(for: type, boundary: boundary) + + return Data(boundaryText.utf8) + } +} + +struct BodyPart { + + let headers: [Header] + let stream: InputStream + let length: Int + var hasInitialBoundary = false + var hasFinalBoundary = false + + init(headers: [Header], stream: InputStream, length: Int) { + self.headers = headers + self.stream = stream + self.length = length + } + +} + +/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. +/// We encode the data directly in memory. It's very efficient, but can lead to memory issues if the dataset is too large (eg: a Video) +/// +/// `Warning`: A Second approch to encode bigger dataset will be addes later + +public struct MultipartFormData { + + let boundary: String + let fileManager: FileManager + var bodyParts = [BodyPart]() + + /// Creates an instance + /// + /// - Parameters: + /// - fileManager: `FileManager` to use for file operation, if needed + /// - boundary: `String` used to separate body parts + public init(fileManager: FileManager = .default, boundary: String? = nil) { + self.fileManager = fileManager + self.boundary = boundary ?? Boundary.random() + } + + /// Creates a body part from the file and add it to the instance + /// + /// The body part data will be encode by using this format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTPHeader) + /// - `Content-Type: #{generated mimeType}` (HTTPHeader) + /// - Encoded file data + /// - Multipart form boundary + /// + /// The filename in the `Content-Disposition` HTTPHeader is generated from the last path component of the `fileURL`. + /// The `Content-Type` HTTPHeader MIME type is generated by mapping the `fileURL` extension to the system associated MIME type. + /// + /// - Parameters: + /// - url: `URL` of the file to encoding into the instance + /// - name: `String` associated to the `Content-Disposition` HTTPHeader + public mutating func add(url: URL, name: String) throws { + let fileName = url.lastPathComponent + let mimeType = mimeType(from: url) + + try add(url: url, name: name, fileName: fileName, mimeType: mimeType) + } + + /// Creates a body part from the file and add it to the instance + /// + /// The body part data will be encode by using this format: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTPHeader) + /// - `Content-Type: #{generated mimeType}` (HTTPHeader) + /// - Encoded file data + /// - Multipart form boundary + /// + /// The filename in the `Content-Disposition` HTTPHeader is generated from the last path component of the `fileURL`. + /// The `Content-Type` HTTPHeader MIME type is generated by mapping the `fileURL` extension to the system associated MIME type. + /// + /// - Parameters: + /// - url: `URL` of the file to encoding into the instance + /// - name: `String` associated to the `Content-Disposition` HTTPHeader + /// - fileName: `String` associated to the `Content-Disposition` HTTPHeader + /// - mimeType: `String` associated to the `Content-Type` HTTPHeader + public mutating func add(url: URL, name: String, fileName: String, mimeType: String) throws { + let headers = defineBodyPartHeader(name: name, fileName: fileName, mimeType: mimeType) + + guard let fileSize = try fileManager.attributesOfItem(atPath: url.path)[.size] as? NSNumber else { + throw MultipartFormData.Error.fileSizeNotAvailable(url) + } + + let length = fileSize.intValue + + guard let stream = InputStream(url: url) else { + throw MultipartFormData.Error.inputStreamCreationFailed(url) + } + + bodyParts.append(BodyPart(headers: headers, stream: stream, length: length)) + } + + /// Creates a body part from the data and add it to the instance. + /// + /// The body part data will be encoded by using this: + /// + /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTPHeader) + /// - `Content-Type: #{mimeType}` (HTTPHeader) + /// - Encoded file data + /// - Multipart form boundary + /// + /// - Parameters: + /// - data: `Data` to encoding into the instance. + /// - name: Name associated to the `Data` in the `Content-Disposition` HTTPHeader. + /// - fileName: Filename associated to the `Data` in the `Content-Disposition` HTTPHeader. + /// - mimeType: MIME type associated to the data in the `Content-Type` HTTPHeader. + public mutating func add(data: Data, name: String, fileName: String? = nil, mimeType: String? = nil) { + let headers = defineBodyPartHeader(name: name, fileName: fileName, mimeType: mimeType) + let stream = InputStream(data: data) + let length = data.count + + bodyParts.append(BodyPart(headers: headers, stream: stream, length: length)) + } + + private func defineBodyPartHeader(name: String, fileName: String?, mimeType: String?) -> [Header] { + var headers = [Header]() + var disposition = "form-data; name=\"\(name)\"" + + if let fileName = fileName { + disposition += "; filename=\"\(fileName)\"" + } + + headers.append(Header(name: .contentDisposition, value: disposition)) + + if let mimeType = mimeType { + headers.append(Header(name: .contentType, value: mimeType)) + } + + return headers + } + + private func mimeType(from url: URL) -> String { + if #available(iOS 14.0, *), #available(macOS 11.0, *) { + guard let type = UTType(filenameExtension: url.pathExtension), let mime = type.preferredMIMEType else { + return HTTPContentType.octetStream.value + } + return mime + } else { + if let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, url.pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() { + return contentType as String + } + return HTTPContentType.octetStream.value + } + } + +} + +public extension MultipartFormData { + + enum Error: Swift.Error { + case fileSizeNotAvailable(URL) + case inputStreamCreationFailed(URL) + } + +} diff --git a/Sources/SimpleHTTP/Request/Request+URLRequest.swift b/Sources/SimpleHTTP/Request/Request+URLRequest.swift index c72a0bf..1af4191 100644 --- a/Sources/SimpleHTTP/Request/Request+URLRequest.swift +++ b/Sources/SimpleHTTP/Request/Request+URLRequest.swift @@ -27,7 +27,12 @@ extension Request { urlRequest.setHeaders(headers) if let body = body { - try urlRequest.encodeBody(body, encoder: encoder) + switch body { + case .encodable(let body): + try urlRequest.encodeBody(body, encoder: encoder) + case .multipart(let multipart): + try urlRequest.multipartBody(multipart) + } } return urlRequest diff --git a/Sources/SimpleHTTP/Request/Request.swift b/Sources/SimpleHTTP/Request/Request.swift index 17e4112..5f6cde5 100644 --- a/Sources/SimpleHTTP/Request/Request.swift +++ b/Sources/SimpleHTTP/Request/Request.swift @@ -7,6 +7,11 @@ public enum Method: String { case delete } +public enum Body { + case encodable(Encodable) + case multipart(MultipartFormData) +} + /// A Http request expecting an `Output` response /// /// Highly inspired by https://swiftwithmajid.com/2021/02/10/building-type-safe-networking-in-swift/ @@ -15,7 +20,7 @@ public struct Request { /// request relative path public let path: String public let method: Method - public let body: Encodable? + public let body: Body? public let query: [String: QueryParam] public private(set) var headers: HTTPHeaderFields = [:] @@ -23,12 +28,12 @@ public struct Request { self.init(path: path, method: .get, query: query, body: nil) } - public static func post(_ path: Path, body: Encodable?, query: [String: QueryParam] = [:]) + public static func post(_ path: Path, body: Body?, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .post, query: query, body: body) } - public static func put(_ path: Path, body: Encodable, query: [String: QueryParam] = [:]) + public static func put(_ path: Path, body: Body, query: [String: QueryParam] = [:]) -> Self { self.init(path: path, method: .put, query: query, body: body) } @@ -37,7 +42,7 @@ public struct Request { self.init(path: path, method: .delete, query: query, body: nil) } - private init(path: Path, method: Method, query: [String: QueryParam], body: Encodable?) { + private init(path: Path, method: Method, query: [String: QueryParam], body: Body?) { self.path = path.path self.method = method self.body = body diff --git a/Tests/SimpleHTTPTests/Encoder/MultipartFormDataEncoderTests.swift b/Tests/SimpleHTTPTests/Encoder/MultipartFormDataEncoderTests.swift new file mode 100644 index 0000000..309ab28 --- /dev/null +++ b/Tests/SimpleHTTPTests/Encoder/MultipartFormDataEncoderTests.swift @@ -0,0 +1,163 @@ +import XCTest +@testable import SimpleHTTP + +class MultipartFormDataEncoderTests: XCTestCase { + + let crlf = EncodingCharacters.crlf + + func test_encode_multipartAddData_bodyPart() throws { + // Given + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let data = "I'm pjechris, Nice to meet you" + let name = "data" + multipart.add(data: Data(data.utf8), name: name) + + let expectedString = ( + Boundary.string(for: .initial, boundary: boundary) + + "Content-Disposition: form-data; name=\"\(name)\"\(crlf)\(crlf)" + + data + + Boundary.string(for: .final, boundary: boundary) + ) + let expectedData = Data(expectedString.utf8) + + // When + var encoder = MultipartFormDataEncoder(body: multipart) + let encodedData = try encoder.encode() + + // Then + XCTAssertEqual(encodedData, expectedData) + } + + + func test_encode_data_multipleBodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let data1 = "Swift" + let name1 = "swift" + multipart.add(data: Data(data1.utf8), name: name1) + + let data2 = "Combine" + let name2 = "combine" + let mimeType2 = "text/plain" + multipart.add(data: Data(data2.utf8), name: name2, mimeType: mimeType2) + + let expectedString = ( + Boundary.string(for: .initial, boundary: boundary) + + "Content-Disposition: form-data; name=\"\(name1)\"\(crlf)\(crlf)" + + data1 + + Boundary.string(for: .encapsulated, boundary: boundary) + + "Content-Disposition: form-data; name=\"\(name2)\"\(crlf)" + + "Content-Type: \(mimeType2)\(crlf)\(crlf)" + + data2 + + Boundary.string(for: .final, boundary: boundary) + ) + let expectedData = Data(expectedString.utf8) + + var encoder = MultipartFormDataEncoder(body: multipart) + let encodedData = try encoder.encode() + + XCTAssertEqual(encodedData, expectedData) + } + + func test_encode_url_bodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let url = URL.Images.swift + let name = "swift" + try multipart.add(url: url, name: name) + + var expectedData = Data() + expectedData.append(Boundary.data(for: .initial, boundary: boundary)) + expectedData.append( + Data(( + "Content-Disposition: form-data; name=\"\(name)\"; filename=\"swift.png\"\(crlf)" + + "Content-Type: image/png\(crlf)\(crlf)" + ).utf8) + ) + expectedData.append(try Data(contentsOf: url)) + expectedData.append(Boundary.data(for: .final, boundary: boundary)) + + var encoder = MultipartFormDataEncoder(body: multipart) + let encodedData = try encoder.encode() + + XCTAssertEqual(encodedData, expectedData) + } + + func test_encode_url_multipleBodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let url1 = URL.Images.swift + let name1 = "swift" + try multipart.add(url: url1, name: name1) + + let url2 = URL.Images.swiftUI + let name2 = "swiftUI" + try multipart.add(url: url2, name: name2) + + var expectedData = Data() + expectedData.append(Boundary.data(for: .initial, boundary: boundary)) + expectedData.append(Data(( + "Content-Disposition: form-data; name=\"\(name1)\"; filename=\"swift.png\"\(crlf)" + + "Content-Type: image/png\(crlf)\(crlf)").utf8 + ) + ) + expectedData.append(try Data(contentsOf: url1)) + expectedData.append(Boundary.data(for: .encapsulated, boundary: boundary)) + expectedData.append( + Data(( + "Content-Disposition: form-data; name=\"\(name2)\"; filename=\"swiftUI.png\"\(crlf)" + + "Content-Type: image/png\(crlf)\(crlf)" + ).utf8) + ) + expectedData.append(try Data(contentsOf: url2)) + expectedData.append(Boundary.data(for: .final, boundary: boundary)) + + var encoder = MultipartFormDataEncoder(body: multipart) + let encodedData = try encoder.encode() + + XCTAssertEqual(encodedData, expectedData) + } + + func test_encode_varryingType_multipleBodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let data = "I'm pjechris, Nice to meet you" + let name1 = "data" + multipart.add(data: Data(data.utf8), name: name1) + + let url = try url(forResource: "swift", withExtension: "png") + let name2 = "swift" + try multipart.add(url: url, name: name2) + + var expectedData = Data() + expectedData.append(Boundary.data(for: .initial, boundary: boundary)) + expectedData.append( + Data(( + "Content-Disposition: form-data; name=\"\(name1)\"\(crlf)\(crlf)" + + data + ).utf8) + ) + expectedData.append(Boundary.data(for: .encapsulated, boundary: boundary)) + expectedData.append( + Data(( + "Content-Disposition: form-data; name=\"\(name2)\"; filename=\"swift.png\"\(crlf)" + + "Content-Type: image/png\(crlf)\(crlf)" + ).utf8) + ) + expectedData.append(try Data(contentsOf: url)) + expectedData.append(Boundary.data(for: .final, boundary: boundary)) + + var encoder = MultipartFormDataEncoder(body: multipart) + let encodedData = try encoder.encode() + + XCTAssertEqual(encodedData, expectedData) + } + + +} diff --git a/Tests/SimpleHTTPTests/Extensions/XCTestCase+URL.swift b/Tests/SimpleHTTPTests/Extensions/XCTestCase+URL.swift new file mode 100644 index 0000000..40ceac3 --- /dev/null +++ b/Tests/SimpleHTTPTests/Extensions/XCTestCase+URL.swift @@ -0,0 +1,7 @@ +import XCTest + +extension XCTestCase { + func url(forResource fileName: String, withExtension ext: String) throws -> URL { + try XCTUnwrap(Bundle.module.url(forResource: fileName, withExtension: ext)) + } +} diff --git a/Tests/SimpleHTTPTests/Request/MultipartFormDataTest.swift b/Tests/SimpleHTTPTests/Request/MultipartFormDataTest.swift new file mode 100644 index 0000000..930995e --- /dev/null +++ b/Tests/SimpleHTTPTests/Request/MultipartFormDataTest.swift @@ -0,0 +1,68 @@ +import XCTest +@testable import SimpleHTTP + +class MultipartFormDataTest: XCTestCase { + + let crlf = EncodingCharacters.crlf + + func test_addData_withoutFileNameAndMimeType_expectOneBodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let data = "I'm pjechris, Nice to meet you" + let name = "data" + let expectedHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"") + ] + + multipart.add(data: Data(data.utf8), name: name) + + XCTAssertEqual(multipart.bodyParts.count, 1) + let bodyPart = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart.headers, expectedHeaders) + } + + func test_addData_oneWithoutFileNameAndMimeType_secondWithAllValue_expect2BodyParts() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let data1 = "Swift" + let name1 = "swift" + let data2 = "Combine" + let name2 = "combine" + let fileName2 = "combine.txt" + let mimeType2 = "text/plain" + let expectedFirstBodyPartHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name1)\"") + ] + let expectedLastBodyPartHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name2)\"; filename=\"\(fileName2)\""), + Header(name: .contentType, value: mimeType2) + ] + + multipart.add(data: Data(data1.utf8), name: name1) + multipart.add(data: Data(data2.utf8), name: name2, fileName: fileName2, mimeType: mimeType2) + + XCTAssertEqual(multipart.bodyParts.count, 2) + let bodyPart1 = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart1.headers, expectedFirstBodyPartHeaders) + let bodyPart2 = try XCTUnwrap(multipart.bodyParts.last) + XCTAssertEqual(bodyPart2.headers, expectedLastBodyPartHeaders) + } + + func test_addURL_bodyPart() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let url = URL.Images.swift + let name = "swift" + let expectedHeaders: [Header] = [ + Header(name: .contentDisposition, value: "form-data; name=\"\(name)\"; filename=\"swift.png\""), + Header(name: .contentType, value: "image/png") + ] + + try multipart.add(url: url, name: name) + + XCTAssertEqual(multipart.bodyParts.count, 1) + let bodyPart = try XCTUnwrap(multipart.bodyParts.first) + XCTAssertEqual(bodyPart.headers, expectedHeaders) + } + +} diff --git a/Tests/SimpleHTTPTests/Request/RequestTests.swift b/Tests/SimpleHTTPTests/Request/RequestTests.swift index 14f86c2..8d3d111 100644 --- a/Tests/SimpleHTTPTests/Request/RequestTests.swift +++ b/Tests/SimpleHTTPTests/Request/RequestTests.swift @@ -18,26 +18,58 @@ class RequestTests: XCTestCase { XCTAssertEqual(request.httpMethod, "POST") } - - func test_toURLRequest_encodeBody() throws { - let request = try Request.post(TestEndpoint.test, body: Body()) + + func test_toURLRequest_EncodeBody() throws { + let request = try Request.post(TestEndpoint.test, body: .encodable(Body())) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - - XCTAssertEqual(request.httpBody, try JSONEncoder().encode(Body())) + + XCTAssertEqual(request.httpBody, try JSONEncoder().encode(Body())) } - - func test_toURLRequest_fillDefaultHeaders() throws { - let request = try Request.post(TestEndpoint.test, body: Body()) + + func test_toURLRequest_encodeMultipartBody() throws { + let crlf = EncodingCharacters.crlf + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let url = try url(forResource: "swift", withExtension: "png") + let name = "swift" + try multipart.add(url: url, name: name) + + let request = try Request.post(TestEndpoint.test, body: .multipart(multipart)) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - - XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], "application/json") + + /// We can't use `XCTAssertEqual(request.httpBody, try multipart.encode)` + /// The `encode` method is executed to fast and rase and error + var body = Data() + body.append(Boundary.data(for: .initial, boundary: boundary)) + body.append( + Data(( + "Content-Disposition: form-data; name=\"\(name)\"; filename=\"swift.png\"\(crlf)" + + "Content-Type: image/png\(crlf)\(crlf)" + ).utf8) + ) + body.append(try Data(contentsOf: url)) + body.append(Boundary.data(for: .final, boundary: boundary)) + XCTAssertEqual(request.httpBody, body) } - - func test_toURLRequest_absoluteStringIsBaseURLPlusPath() throws { - let request = try Request.get(TestEndpoint.test) + + func test_toURLRequest_bodyIsEncodable_FillDefaultHeaders() throws { + let request = try Request.post(TestEndpoint.test, body: .encodable(Body())) .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) - - XCTAssertEqual(request.url?.absoluteString, baseURL.absoluteString + "/test") + + XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], "application/json") + } + + func test_toURLRequest_bodyIsMultipart_itFillDefaultHeaders() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + let url = try url(forResource: "swift", withExtension: "png") + let name = "swift" + try multipart.add(url: url, name: name) + + let request = try Request.post(TestEndpoint.test, body: .multipart(multipart)) + .toURLRequest(encoder: JSONEncoder(), relativeTo: baseURL) + + XCTAssertEqual(request.allHTTPHeaderFields?["Content-Type"], HTTPContentType.multipart(boundary: multipart.boundary).value) } } diff --git a/Tests/SimpleHTTPTests/Ressources/Images/swift.png b/Tests/SimpleHTTPTests/Ressources/Images/swift.png new file mode 100644 index 0000000000000000000000000000000000000000..72216e8d47aa27f1834099c916c3520364dd5733 GIT binary patch literal 32014 zcmZ_02RxPU8$W&s*&Pu|cJ@};^9b29Gb5vnkWFUxR%Z6fcCrq#$tGol5I%O2S@!zf z&(Y`e|Gxge*YEXuTI8JPx$oGGZsHzf2khWI3b!X z$UnyF*Su!$V$+;xRysX?C)pwPTx;>?Fzvk-@#FjFTcK%lH>(paymZzZ%~kEZpJ_|4 z_pqEfyfj~E{pNo0{q|=@Av-^_AO1)+qLk09;+>RdC;Kd+cB=#3B@`iAhsz{nUaog! zjc-bi@kD2rP=td~{2{eV_C=SSY+ zL(_TTv(z$wVq%vTt7p8d|FjfTo}Wa!1YAfw%)3?}t9Iu&=p^c7Mqx){@oI;6l| z&cX8g`4H;mr@+C|9_44>b`4Ev|Csz5ydy|0Jf=qL{^|X*5S6)|r6FC9V3cEzRtM+X z-14Y*aC6k^K@wr~XRCw*RzBE`QuGPb>Cf=VAR=UU*Kf4#x475N#>aUSF@cy=Q}4G4G#yKl?-# zy%m#bbaCEGv%eCb_MC95#dYrJ&+CDk#gf0gogQ*+O{u@tRrb1BxPgiPk##J+d=+Pa z40C`iZ;>S|NiZmp?JC!~0|7GmCKm<)w*M|^5c4SgJCtOS_|al+(1B0|Kk!PTee z%&uc9k_N^RTi~+FA~E0o(%Hq2+Oe_rb@)STC`ox&@VLA8!qUOMUcuE(Lbzx(voK_~ zOWiJVo7;Ggpu;-UbcfPBD8TCGE+*LnTn;U85ylEck@rTE=P_H2?JAaPNIB&N?_#bH z|MQNB0lcN^6rHImE}JDTs~wC@i_1w_b2~0^!Yl9KezV}pn{M|W@~JcvdsOv$-+Wh7 z?#uAf*qzN;n%QhR^;Eb{0URM6)W#p&hTs#KqSMQ)eL-xnp<|HR^rHTXkab|M1tV4h zb6Bz{EI|PL#XnJS{S{V7=}|M^*WJx;2Whi6MW<$OJDaCw#NO7eI0?@w-&!FO?B^)S zXi}85m; z>J(T&*yHh%_0jk;d8V{E>JyDouROWQv}g9|lc!RB!%?I0O}REP+xBz&J(MN#l=yg0 zEb-fySU?yAsl(|j)QyX-Gf}clf23{A;ITE)+@vW;rQl)lj~5A27C=Z@2YA$9k+Mbz zS(AP^tn$2H-hlP~np$?!T$E4s{D#Ka#^Bznr~~Kv5sxShwcFtPo=_cG6UryjHAnOG z?H%$?ox(fr=%tvwi(^;` z8bUyCG{hfFhtCj14h7$HlpC(W?vEU)-Kq?Vx6gxM@mP$j;QRcOxET3F;749-;#7!G z$~?=scRC$kynI%`;v?*6hbLxs|_-gF-q3}motHaS(4tOiUjfLe;&z9)$v2B(D9!K(xqiP%Np4G zD|7V)3+K|hf)-+JE+@%Aa|? z5Wh>_s-VEqd1DRlq zDoZ4v)5iuen@tCxLZ;1vBj_!bzh>7%m)_Ahn4T)KR9?R^Y_B}#!>5(`?bO}Is~MFi z^ErdJ?aa#P^uiJFd@95M83>>cmjR?0@fONoP?Hb?V^7cD9x6tTsJw3l3^P4jov80IM8IN;{|? zB;fPm0q7x!pPS0LpYYR!vJW>J#l3hjNTtFK!P5LYB6_fyW~wk&72ITH%bc#pP*yz=VI(0%x$W5J zVkZ)j<70v{3hEJfJ%BsgPMBbM{&`33H#1(hSne|Q=jZlaI{}fW<8yr57r(qsw^vGf zuu7$)5(K+(vD*;RWy$x_3jFS|9oofl90`HpGhihM1S!`d z!W3x0rUspMo5;_e60Q5|_Hn_y-|Txz`(tnCqP^hGZ>`D_3!THAC#Xl+j)9gX z)T+MbQB|+r2Ei2}8WWbpCMQSwv7c-)XgBiP)AN%VZ(5SK*K?PN81f1JU4J{=ROL{3 zycp}zAW^bdbm9CM$-%dD3%A<$x4yeiTnSuv+YP~W9Km_AOX}xG27@Af85<;v9&1=4 z8;%?msKr;7DEJ7FESge)EscN&h1MNldysO0421PeB?`uCuiUSh^TghMRvnk3UtPA^ z`n>+x&|E3aO}Vtqr}uO`5rFM1EV1CQ$#&ks1*oOo0QOWGY%4!RDJbtGQ1N>9+3H*4 z=~?G369F8CZa={um&Fb@1zLQVw(4GbQ-JkoTHb!Q@y%nu=f(k9(eC7PBtEA?Q&C`F zK}y1zCHdrpJEaJt1bB49tLB}iI{j)vfk}}bpaNB>Q-KP>8V2|RoHs?a!iigh`0?qS zlUO9rT(u{j8~fHR?W*;2dX&-Un(GznAS7^^C8ghY)MzR=QHNiaYfh;$%~2m-I^d>056V`& z<=}$LW(WO0bg+CFuj^6oSkK$XIX>P_+ho_j?el5&2B8SLuCav0&r!Z3BZ&u54M(hn z0K^4YPG@}`MWY;$B#5Of5b$s<0FW6N_6@)|rI?2fy@o-pcj`^UPp|NU5XJ)KOWD^f zN*#?Ufc1r;lalp{!IxdRjn9!#lnDUr2t?mC$w$B?9#S>?VUD$m?OqcQEgmdLJQ8;= zyY=;hQ5(EMojUw2M6}+X;(&WFPrxzz8F>1aN6_inO?T4P=?=Yg!y1!h+>!g$HGl_5 z5Pkd_O!X%e6@b@*@P2`~-W(c@$g*kPn6{ep{>-_^&O>K38DjOcUf{(rzjb; zaJgilJN|A^#i7a%0+Mx}X7jJ$^Fi539<{{Gtua9C}Q1-}@bmZ)f z3c2&(UYy=9y!_1;t~DwGzF3+iHG#?tTW^Q}J-K{!b+95Lcg^A`I9}I`yu8;k^3m!h zfH)A45P^80NP(qDg$aeSLY&~w8XbL}7@>iu4<*w!Sv>_93i-abXA1#Te;D~MQ2hT^ z8T_c2slL@tmZ+6wQ>A3M(l#FDs z0WvHC07_i~I}=kq9LP`E%L*0!SQY>pu>41GUX2976z{&=!Yc84veYObAHEveczj-` z!Vy`wq2J}M#{&wjRSGDf8uAEcdXzGK_9@n@k?uMqn?E^zt|fk30_#;Slb&kKXhAb9 zh>4R1FppwnyYSJ0%!4tiaFxg8cI(RB*OnCgsh7s|`+0q}h1uQ}89v2gs1X8K{2y5b zsD%XUn( zA)8c+Y4$vo0l4{80Mh~couXgqq_P(PoCf5xs&9bQQ8-^ej#=DQ1v;C!W)NOB`!k*oW$%5juggJx50WJ94FY|$=r z>Px|mE@zv~)v+-sHAPXIi+*BTTm{>@*R+09 z2x@G|oE2||(`z5xbAY4Yq^wg?5TOjwDgZ8^n+!@2e3|S{U?O(@B&~2?t3G$B`pN;Z zH32!kza`-%B>jI6^a|bMLE)2k4?c}5@vjvVi?}5-UF5kjt$nccqQvK&|If@@PY4tw~}QJ2;@vX@sGm z*<)DcPXwG7*L3;Lvz7^Q$p!MXVwq}d*iB4dVPu z&N~&-e%;#j*?j*>yLB;C$Utl`(PTU0oUFvh3f9RwMhCiY{*=#=$%2<@?%ej$8I}Iv zIB`(h+sb-Y@)0+WapS7$EcN5h?)VOu6&0gQS^6@)0XxjV=EDQ6cvyUWf1;0dkMpXg zt#h6VxrVI=#r@N#7-JG`jbUnpfuLLgzynE1-;@r-3poGHqUHR>;>~)J;UCrS-6OeM zxmpJbmSv+2brawn!NUkZM{${3F)B=>R6}Wg$iuMTkn`dF4H0HZjH2&$kB6fXCX)8f zu2!inUQzBOkwUYw-f`|ESr!)GG5j&Z`Qkv5f~qFgO-I_(0~1UUx_UVs-d4PI)eGwRBZL*VFSx9hpy4tgaq02nRF+ z1QiKX4;Wy|fKXHbw8Llki+12kI-wPm6h(^KexUC_KX85e^qOSv?=ls zD{vl^R2=X#fEhq^(LBeV)EL+-*q796k&hIjBzr z9WJQiSfCpv`5zz&ndg;zK1zMXYTBGdOUb8Gr{wG&(l1L_zr&NFvO?To7DNY3N*bc5 zppguC)Yj8uU@Vr&BmY&F=H}_evfYQu?F;0G`XaZ1-8iV3a{+RR4aJpwiJ{nklY#P5 zb^vVP>l;vVIn}5fHsl^%ocj-wW+u%@YMezr+p$r~9PfZ15&`-^NA?h`B5rC_&mmpg z9sh(zxfo+=mCDr^j^5G_l|^4_Z=9AV`nyzTP?S926Y+(*1CY^Z;`6VT1_?udYxOm~ zv`x;Xf$KIR6_YD?YPQcMf!-Kvv$d||8cW?%%02tTa~T`dJJ2a#a_}(a0`u1iul37k zw}16IdQv%X-)`KBX%DSXBnORUM;=iwb^xe-;F3B55(O>-P&YK~{E7QXr!Q|>+-mDY zclsU^rSSW`&}mAfulB)@*Fs?PHK(BK0S4Ba5uD1t7c@;jkd@xTX41yXT)|o;Oa3Q= zvUJ@Kpynelj|iYGfVNk`P<`_c4uCplbb|$yO>R;lOb3}bzK z$WSFpmf36)@J^sB|DRnm{nJfZ;`Ow%q$wf|KRGB0$PlD<0Q;h1n4yjbEZqMUkh%UO zrH^OR+*(DZnc0r+Ih=mPEZUb28Q=l&0B9Z12NBYs;A3Uuu?vTEnKr-C_0^geKe0^S zX728#!XoH;YXurvGZ(1M1V#IQa0=4vAhv7J`OeL%uln9gExRsSySO;DfzA8c7vHUS zv}i|U34;Bh4Z#5$0-_xDdhW-vhEvq0n&%Bca*0+oRck5J9H`BN9)m1!5im$W&;bc* z4@6F#w+aIN2By~yv2`f96a~ZHc0Tk3SrzKm!kbuBGX|arHU7Ntc!C?H#*h62iy9M9 zKSfd)YXw+1m|}P?Ma=5f{ib{6X8QAuUiRh{_7&zgPx+tXYAf!>se9UNlhZ;OB#eH4NR(wv zA-_JnxbVU=@SBAY=rloj@dvn-P1YwG@EgBW=X1yL)57$75$Zrc0^D$b%sdyUx)Bg~ zR5j%L3!XmF-a84k9uQ|4?xs5CRgc0z5PudxgZ>y6@2}ZhUQ}_S9cp8TIzvj08nB`NhjPcdq5u+fSVHx zsYT?HfFJXTQ8}>XR@pZ0*!R*FJ-K)~g>n)~fT%q4Dpu%0}1`c6Q_&o5Ee;7ZXZJrz=3NYHo_biF|4zJ~*&2mMw)k<>dbaUhC= zr3C4f8*HWww@2d-*4mqpy#PZ(vM6-N!lEe1xthB6)(BI>w+D)<8o0NfR& z)4#@irRd3oL0Z<=f}@_gqRkdSfL`zhc2PxVFB4%fLwlXmhk(=@7CRV=wsF_U$8z_6 zId>U)bH<&J^zh4+!*1vAnh328wAy(;Uo&IfbC<%<-DOhzyY~bod+U3ajs!|Bh`2y< z7r0m?AzmLe5IqGGg9jB8L6rGxxM0~CjU2@yJGG4hMV^}<1efbMwF+SXx%^P@`6 zEcT@R!UitCy3HRdW1`mUGS9Ro$7U!&lRb-mzstu1?+5V;1e-opvTOcOvYK9JVq@IhYY%-lDpl(C3pZlZ6_6@><92U0@1339h?o(5 zJ$ws)7=dm~|F(}ZLe{od#`mlPfC6TA-C-!D7hAn+2&)8~9X!Gt9 z0>KGza|~uYxzCm*^w0UJ5&i17%hC=%dy5Ay5Q8FM+!+uQ)vB|M?C`L3CQmdO4ZVnJ zI130{K+jmc($|0Dkj(}DUNE8p)SvG|m z6MKa-JSK8pvn{Xs-~Z|^MF#BChJpaYBG+~Fk(`~mC8v@@y+0!+;WgJdi6~v@_Ld8i z9H*S)2GGokBM&u~05UIi9YUSu2*i3-u}!HD0oL7Uld`D1Wo$4hg>FCvmj^IJpx<&P z=sI!*x-!m5mz&=~&$EI?xNdYFn)jElsobAyQOx1VTAV50jOvD+i=Lki^@sxTHtd3) z)o>)*O~M&(u6ep;sqfX?tONCEiC%YO1-Z!SPRWvaA}F|EhVZL;Q^3JL)0xJ1{{sn-W z0Q~}t&7I2Or~EXnX3Tp4u&Ns}dOBF|d8$M#nEi(M)yHjNvCgebRb8Cule zNss0Q$@Okb@ReMa>C-)EX4kcC6=?Me67CFmsgO6Ee75Z>_8j)_(|$*dKhl~ZSXFADZ&=Nb(X&L8le&Q%LM(xdxBIOC1NjWYeG^n2=YdKNhvm!4?Ky&h7 zHq-S3nt1??KftQ1^g|2P#)U<`5Av!gGKWk19r?GVn0RyO_06FhiT1Lhe_-xJD&*Y?<(@!1!;avYB(BJNHz@1MD824kVzV|s| zbp8dtJqA-QJ$meoC24z=c3`53=47WnHRxD@#9}&~g69?No^>zQuM}dtsV2D_)Sd;~ zY|)ZkdzGLFz&=W`1X4gSoIyo(gO^e9{@c4?zX&kO`oe*HPj=%o1%GdO!K~iEgY-fU_v4lhR{5G5afj;U)-_*H3e+kaPqa3!$Q18O!_?Kmn3|8h7-~qioe)AdMX<*2N4*jB zPvn!651`B^#Uxk`>3P(3|LkzkLwt8iH$rp)SA|b@y$NoOa5wT)?o~v5PdVX~~$Cy#-4W1i1t}3hv z-fsrM%Cq@V($LRVcHAwvn?7tLeE52Wz??+Lu$buuorhVz-@E>_s~SgfnNe54*q{W+ zf+LWS3gW|09%`Zl^p=&pM)wN~zD?LaEnF*fXLFO646&tz>@jHJ7tlh=RBhX9`&gb8G^tg-T#<>rbTjHd9v5eMPA;rdPsoi9T&NOI<=ucJw?C^ohY{&(U=6yLMY+XkeMVWi?-P7FXf7 zehESnhagD#1MM^*zf#ub{-@*QfSsZsGK>kNW_9W)diK^bQM>vR!gUhitN%*U?I=uRRZ;9}qS8zkfrB)jp^I+OQj*iH`) ze%T6ijOr=5@}$dGHeNLYBp7g0=>(l9C6Q;puN0;4vcI>ecyC_uej$1WOi%H1f)Svz zg$;bA_x7-?4$~Fj;jG=#uq%8Ln|@Pr_V}#;x-P+VG`nifkU#5?m42ANdlh0EO`7`= zN||>jS3bN2;(F1)lbPKWY5Y5;8~V*(TXgP9a(RtwZTJKh@6TSxkpqRoLAy;B7HVr=mTW?FL~Cy|4us(pGq&b#%iBRKRCGEfnqhVG5^?DY(#ZmXqwkfcEXO-Sy?NgPpHaBNirk zDOm-r%^w>>niqSj%ghb>7NW&SLBW#_!o;1ocz6vo-{jED9sD#+MSnR>FGu1g+1Xm% zzER29uZthX=Wp##MSJmzyhG)MtYu88l~tRCLiSt2!F@2Fo=u;6?bWFUJjzg7k^O`g zin^9pqkM|N9!sx(ly~ zitUTc(idklmjJP}IsLx-zlu?<&b!v%>r!fGMKdc!$paHqC;bW0+o8LQ839OJR||Qy z;GnHU_O}h6qWzYf+kt_gGu(0Nq4Hz~d2t2z2rn==AWWg@4O(ABfEF14(Sv+~nEZ?{ zT&OKgkA>I~qffL{lJCcGxZ#*=p1Z711*i7gJiku8^E(c6{j^bkv_ooIJ`iipSSr8+ znn0q?bf9EE4P@G~M{NxMKAN&Uv8kARdwlTT0_8EWEintn2K1R-1B-H zXTfv_$IuabagjL3vLufl?FH!K$nYbiK76;4vOQ~UXI;?jFlUBD^_R!V9~?Hsf4OR! z@=?78`FwsDwbgv6w0M|uM*F?%AJ^r%u@{)n3*hln(#$(w;4Zj1WO43#aTDJiR{X-M z#F6teO&Bo?Mjd8^k8(jU(3z=5A)uAeLYFXh(${DX2Ws-N63#AuK9%gb?~e?7Wv#O@ z4sBmc$yuWHt}NTUWiY-hp*EOUG2}r0P|KQ}(~tz@wY^nK^8Dx}pL&xc1F8u~alMgm zv2R`|r{HEM@Z!`g7^9ov6TZ~g|FN`s&XVIZYXCtH5>c$1V9v}<3V z#ONK}@HwjT|5;aFy`{5K%yn_suECS6ymsji=DD{WtjvwiU|DxRr{7Yh$g%ap-K!Y+ zPu51#8$ughoY#V?f*}x50;UR}I)dNT>*FR^O3z0L_G^;Piv8@!yL9$@AZ1OFv~z&^ zN?bThb92GeXwcN?^NSBMKzIXY2@n$V1t2xPN~4zOysu4pPxpaM2UhYj2=$d#F-L!*_9rSwAjBb_^iN;gGHZ7i`Ew)Uw>)(b4^N?fXIWx*| ze>M$E-9PiuPI?CAyzrwQ7Q%;ofP)4O&l8bLqp1|w{ND^KSQT+)?pIe^9lmAE+?{O( zefSBP(Qv5Q0bw9ux0fUeZ(*6(oQWFKmd`v0)W-l&Bb0rCaSO<tgE$S0Aj_k{GFPt@fog>tyQU2!LV|`i^BmQAv zjhF%43wj>Ai^&W|#de<>gDZ*rP~|zqspI4&UXHh99cW?g=r$erhcNO1RKLR}zy`fT zJ3sJsfgnt{Rms3^_$`v%fwH|cje5UXiO;EJ{O5Ghb6?%r8|yukJFP35MyjCiqj>D} zLUL?T|Fd3s)$yP7L%a&zXFsv@WI=rbk@oz<(iyvLi)UAhn}~g?pkI$E(YtRYIna;` zG^g3w00(y4x=e{IDh^Fjx{fxU>{7u7DF1Vpkt{BGjOQC!Pm8Z=*ve%19E_+8X6YK5 zv>jqk@=xBasf?)9d-!daY&q^6?hL~;QNvo7uz^6t|U zian}l)5#hxo|1BrKn{7K*+ZuQ2Q&*>fy8%0db+zI6=CghoFBgPaRWY!2?WtgM`l;i zfb`)E4|nC3!Ey22E~8ybw0p)C|NcnjXq7E>*CuQ3w;35;pxHIb~pK+9I4T7uad_v(_J4EF1%CG@&4VYx#U1Q zWg>hwd2pVgT{|T4{>E3l3iM*-xLnaw*lijZC2+TIs;jKRjpS&Z zCQ6XOayjy3*QPY=mYeQ3@b-9q7pkWp#u-jNBsrf#18f+FIgr;&K0Jjz;-M&K*73dM8m*T~ZQOWVw^ zy-H*ag4A3>`mnD?X%3HC%>&P-X=$`tkJ%$;r+A0~J_4)VL$-j2Y`?FfY2YhMP<%1K zNVlSi?{R*0Os;tOyMs-R`K{R+9R;W7%5-93?k z;AoB0eT7@{0D>|gkqhO#Ph79KHfHzv*Yw0mMiSC5??(h?#Efu)u0d0!myEwcK)7?t z1`HbL%#K_IkWT=4RY~G0Q(Q0s3oCr#I z7i=z1k{L^ zAE3VWSRD*ZXiVOom3{m*S@%WLt9o}Cq>+a6E?MsGz_I2`!9TkAZ)hdv6MN~{4Tmyb z@dNQ1v>NDW&{|I3%l;uLQRz3`0Yv7DBzJ-nGvHMQN&?VdVcJzd+4ws+s^Cw=1D@=0 z%V2GR*4VwdVhN`onW^Q*i;u>oW@RmITTl!~Ce;Cz`%7gui(TB+D-0(a<>cS*{bDaf zx~o*UNg}saC6YqGhy+rOz=klDhk&Sc_%-*3H?;t##K#-6%bueXo*!?z{5Thyk%O~%)HGUeFfbIU z?+0Rt(xln><(u{xV7Iu`xQ@4^$0z;4(p%-%M{}Ti@f3<>M>-cJw( z#`ggQ5W#GSrw3MF;6GCj2^j4R@!!BQiy99}F|wL2Q5ZMuFBIi+C7C+IT;xr2)U(=R z=y$w}l~_nr9b0hLT0e2F*sk{D+LxWJ>4}$SA8I0hbg}CL2M6dU{2!(@U6&gabokaf zGk+g>zmYPMgv?Ml%*=f^go7S(R4XIeEa-4-VS$g@8fmKtMg+ht4^12(LnF9=l?zjk zI%+u8@!rPEbDNXRq;u_yHCNljM*?+JCc8K*5nf~UY`0PM^) z$APoU$?C&`TbGRutbcl*FiH7LbYhpibPjrH$T|KAFFRh0QB!*%w?+}zNf_6x^{ydy8dPE${Ilc7-HaVRraOGz#GIfGi9k3^E14wSz( zdT>6wVMaEB2WGc=DL*Yt?EeIPdgl~^S>o+d#Gi+_8131`~6tyT^wjY z4{bG6_Abve@R$-@RbMOP*8aWfzR~#jc$B>-*0}zaJ4s{bwcUX=*7t!I8$)q-cRe4~ zF4uN$*<|daYDdlwwpLE01zWhqSlSG(u4ymU@s5@@?w(}v{pj3xO! zn_CY^NtJBvfF}%vOd3u52ge!pY8oeQytH|9Hkp#vJ13DFt(&~PHIwKk5z-c&RM$bs zM^NT4SLGMq;e5;2ok!N;*9j5K(U%28D%vOWH5NXN3?e*Krap-tLtF~quk)v!N1I;R)qnh~iEF(QAT)7<*tTVrt zq>OiqO#SZP8(+Jz9C@tYSg?5&D?nO#p*+nVliZI>I9~EN_LI02NiRx3YbJhG%f(Zo zjWrq?7xLfa6b68!SJ3qOsFUz=-Vc~M29k(^Rz;L1weLc?;2h_ALHPL}>_^6u5xN?f zSg-+AH;n;LrX(cGQU7QVd)dS_txVYZQdhsWXj}SYI_rhCqdtC0h?3m{>LEx5ZyFdJyg@WIp zN3Un_9~&iA`;3_kff;Z7`|>jT&z`#d6gZPga}ZB#-Qu%zCplZ`V%P8`S^V-Qd98T* z^x8?Ggmc=Yxa@AD+`8o*(ePE(=A;_ zN5B!q_x$MW-ujX~^4SgYIUw*w_<4j2N}NknFEQC3deQuX8w5>&OZ z;pp}bHCcU*AYk}Y%#d+MeTt7CG+lBPD|~v@9vyB7@a&)kYU58Nx1e_RE_=@Hfckx` zVW(eFDEdL(`1XrAQTN{mKdr?zt*NJ-Bm?SS_}7e-oSVDN#g^Cpp(=SS5TfhfG!-Y$ zO9C1`U?6?f)fkv&Pou;|#CK9C)>K&kXO7KD>%tB6G{E``$A)7nz9Axacu+D$doT6cWeSQ6c`oKlP{o>ir*$rNoy zhIPrL5~w`388s%g>a+Td8+aAh!K^i)Pkz_?q3{hx!^YkSCXaGFcIs9z9%+-xku5 zGkbjp_fZvG$~r+;B)&a$z;w{UI~};P7l`Vs8JWRp7n3YwFz|T1UU;7BChn8(qx9LG3^ijps;9RHCJ4%(lkehu0dYoXXLw)ub#m8^s7lTZ>TF(rr+S}a+rD%SOAk^| zUtz~-aT9wlTnsvjI;9Hw^l+4UN6ZRfF`^F=!5&QQDn5gOWlcZrrgC}K&1u=WAdT_} zZhZ1n1Ld&CezN)YsQ74~ddg#2eTw&tQcg?95F6z?rGbj%TQ8ctn3rkt`WCM%)-=uD z7={Ze$5O@>{VOi{+a|TvVmC$hmLeycrVQFj z#pP_b3^_Jpd(S(^&)=|@FV2jo5Eh@DR%%}{tqE_HpA7iyI8x;P)A+5-8>*|OMc1(b zkq5miJKAdm4lV42L_x9#x_Tv0RNi@=_<3!hQFexPx9*ojA=l}K7s|2kg|XcZ_hkMV za-c*l3w0rI!ni79=Vw*m`d;r;tV)P8H10x;kwR~mb-CL%ME?ufkLRP?lg(uMQ=z#W zlvpg#xL&y+P>PAQ{12e60)h7f-@FKgC>uXE@X9-U52Zr8KH>44s3oqBmpc~+c@4YZ zO!C05sN3>!i&jpWwoRbG7smKVH(sHYp!74VOP6OJ5-PMnwGU(v#l^mB2r^oHB+ zyKjgAy@CZ`hy&zg0sf1W=Rp;5f9JMfx&dMuTA1@fU*uO6RSN@-(uj%x~h0Kfn$tVpI}UH;jCK_P(Oka5Xh)#*(br6)s2# zz!I1)V9GioxZ|8S>t?gt8o1nK2mlrXX;Mp%3vOTWzx08FG_cORt@IO-IFVtMVUe=& z=6CIWOKrL$&n5x{c|@+{+3a~*%uP>8ng;^FgYgMZU$n8rPR|v` ztH?x|9b~Ewr$w+fqzhU(0EmPfQmHT{VsdD*25}=WPy!xg>`SeBrpZc0IJNe>Tjt(K zFEwAy^4#c(VfQeO^g3%ts7LPz?bpDN?nuBx+uPx*+Jf+(sEkA$o!2;nY*xshxkm$9PTQ`ptpOQoIqV z#Cp1MvQO*w{cDo1Qd&}`sW|V8?WG9|>9Ru!$yVA&kA;gy#nx`BoO#u$FC9xz0?_~* z3P?v`ypBuM6+vFO8$R3Zeg#Yjfb3q506s3UwAcgnL<^UZ7TLJ1i#j=q5hpc_DcpGZ z6mQJ?H#gOI<<+7z#;aDVq>&4;HlH^JSa)m&JTsXw2VUR)H1~4VGs>y-EKzjsp6?!} z?ECd-U-UYMtdQb-(=ccd9%a zmfB4Yn{O@W72kSgvLv5nglN1}5bW*`P6DIWJI@Zbe0=K9#za={m7fIY>YcH@T!+U9 z+PFh9E0|IUSv$8y7>oWmZs1U~>iFubEdWf6(5MI9VnSUeaA7Q(;pZ%aVVbT;spJ+7 zjcm@G#Iw0aqV{Ds#-CKN7f=5IYU}Tts3%hzJ9qa<^g@=tMsJ%)RJCfZrYBL`vc1&e zlXbX>KHi#(KFag()h>e)&ddWrYI#G?L{~VMI|Nu^BSbl%sYhR!%Pb)*H#*a?kSRW% z9ZS)_c!iQ&gjAG#65Lu}7@bC)2{(K0C79M&vcwALaB>$Elw|DduBDqdN1tuvQ6jI) zMGD%0<-Ft%(qLjykwldDBTgD+$((mTc(@^K-qBqM9JWB)A0UqtI6d`%6)WjRIsVf> z1tXDO8h;A?>b9!t9Sc_0>x#`ZNIJ%_8J6F>JawOXd2r|CuISFaF|A)Mj$}qUlvfFY zFFmPP3As(Z%Y@@Z9jq_M=**g|Mwzbg8L;wL`0~m8rRPxZ4wjT^(c(eWg+|KksDa~{ z-D!Kv#7cKcNX9a1FXp#(hioger?3q{Q6SH%Gpn#^>xJO8Z&_pYh5I|jWcsPGm5P5S zmY_5f2Qca38O(Fw49m@KPpECm=WCr9N|z~9$3XsFh&qC@7u2U_-(+zBV21PdM=Gme zpSr>5yU-we3n^OsCvLMT?q{Fg4tH)~(jB$H)Uo)V?B_Oqa;upQ$>bVK4d;|ht$+`L z*0>idiLB9vckHnS;>PGyF1Cy*)6W-ygivfkEmQz6+v3=GNS~3_;-kFcwKn3D_i0oI z5Bv_(%d_4zO^Gm{^GgWmSysOsF0d@LSe7NN=G!ZEc-LFw!zU#T8A)M^c|v+hH#v>< z!2#B>D6;?34WVH?5|9HawBZDfDFct!XGLNMscah=ydxpp0W)ugD|`dGiCI`&LWYx27<>PbV> zby@ca7NG~qC#!tEuJ1ID+tQPVmVN|F?hhIVI4hBO6i0z;+`!mnh+>BVx zg~k`FJ|zkT^0PUtwJW_MOz(D1>e!rIw`zfy1}uZ1zsKxX*Q~fBZM&5(+qB-UWCP5) z;0|F)d{{cUnzG7&J4`Uw4Xgticem-Dc{)yA2fGF&m-DWXuL4i@1J5^IgyXLG6~D}Mi3_N!@vSnNM{KI6 zMuIL&3Dn{vJV~7Y(h|%8t+hWbO(VzmsXZ=8PV6(%7+q79E_zM(3VWjo$D=9HXHI}$ z4Z~Y=4X&5W_Yimsr@7g(Yq(MBXzwJHh2TI7*1*^Ku(uXcr9Vew{DMQjTiB;goO8E) zbOMC8=-qX|wm%L~1GiLPJErVSBS+7n9np*?QrTg0(GLO%DZ=DD5iBL>FLH2ErjwOPhKTIO_&DU{G=gBU_!^ zz`NdhO3Hx6%BUCaY3_20H4KFNzhyKyTTvTg-V9lZsv9a)+&>WaFV2&ED83B7xd*nG z?5gce#sy-G{Ag(f7RbT!_A5@AEoGT3;hVrD!D~9I0ZET_j1yp7T2ZkAxDu&z2U>AtBu`AKEk z`tG^ljuN3YFB_?2TCJ=Dx zRu153?FC$CH#Z&6=4rbQ??ng)_Lrca$+Yhj0ztM?>>=Uow1TXm7`tZe# z@(Pb+xHK>?ppCO~EMuBj1Ltok5tU?UXF_K~_0& z49r%0|IBfPj=$k`b>Pn1PQ)BH)oprHNQwZbh7{wkvI36-Q@cMCp25|X8^bs_wrBN( zf&U~B@&E%o#1Elk5|ES*UUxSjkq$ShSRjngAl}J}8c*xwI|EMXHQ6M7ZB42V-*@D) z&?n%)?5Ql6kQhHZfm7OcE}emZlK^~{%q0&4yp~UB>aS3tR{#UYXl~ft%5$a9bA3XL z{47>;O)aN4?i_G$Y!YWPxF$G7f;XS=ZpkzU9J|CqHt#ldILCn_wP=M0$SuI43*1#8 zf4~JB(B?;dG+CaNC>vGLy3(~N7N4uTs+FMObai+aXUECx4)k(FQp*VxDPK&Dx=guD ztsoPv|M+G~Fv{MnRp@fQ3+Xrz^vcOoU0OjwMXun#5vnxwv#+kNvOZo(bP}brRq#!P zk?o}liER0^qVFK7ag#;CC%berj)AiWkRwI49}Agr7vE|o$QSiW*KlJ9eewz*Fgj7A zVxW(vxU{Xn^g?g7wW{%)hvpLm`yP}u3oos_$V#%ay;+KgFxDNR0Y@V&@WDJ2I6ku9 z-NNjDO$;)fRr~o4-4vgA*0_k3FeFX?8}?{NEqHZcc7fa|*){eH!`gd}n|PxcPf~qP zxn#Mw22A-xu7d!`3kvY?3=C5sR1vAUVoMX~$`p7%%hB(R-brP_YTgBfQlEw5c!e@Dl&?s z&}Z;?2Koz6(j9RUkKZ5!LxRY7DiC9=vKn$64Mjn<$~P(HZ5d>izY_C?3#MPTn1D8z zy+8rB0-nwDpDEtrYe-Rm;0KAK8!C)~p^INzeXq!pA9O!tB7iUlA^qXIbkp}IMd`d? z1CvQ%_~5e|>{9Rfk9!X97JveUt}M`+qlL1+v4hX_LglBDZO>VNn`F(WSurQ)4=Nin zT5L|TevmT@m;#}_6%0#`Cn&qc1q^v%3OJ~xDyQq^sLwxC*L(J&@&bnt){w{JGWRZn z6aDI7wX8KITQYp5MK=vyI-9h1-|!TX+WZ=15~LO%O!LeBk$6Pb>;{(uqy~DB0y<+i1u&S-MhU+d{3-wD(cL2Z9=WNP z1Ih+oA9<|B%RdMxFA*t%A&S(|+G@;A^E@QAUgmhl5yzCvp8i*qUubjRQUPP(PU!?| zRRs$;P#}U&ACCm5p(vTZ(pPZPBIZ52b|@3I1B950pDOA`_#8L6E{96^B=$2W4^36J zz9U*ZLCFqK{hz9?1DwkK{YxaP#0jM(WR<-OQ7YM6vNDRJ$RhAZHk!6IwaC)D>iuI`R?Y#P?TDHJ=1EOz;u?3Qnsn#ha zkn8y6a+gQq_w?Oe(J%yXuqUg1Rul%$0jp<2Km=`RE_eTggdS_GNCzB6skWW)|# zvxU9u<5FUwUd5eZ>-kx(+3wlm+=rB=>i<0dP1m)yKIwlLR$Wcguq58AWg z)wi~^-qG9Xt?+FwKE{heI9TUTvo*%_3meCCI*#a*0U0~ln{wm8`a-2Z=pVA|vH9tN&3{?s5b;*${j6Z&*cxx)c#ThZTSPiZC>)&rds?db*&$(FnWFpE zW~4{w%+$fsG60<)Gd%z6bivN5RrSFOi`%;Z$Z@*DOZG>5;=NY$krM zS>|H!#rAluid5HZww+lw-9kTlW(}?>$Yt;R={a5O1p4b(={Zedb@0<9J z<(a03Hw}10peciDuc_H@a<4byH_|@^eE@%X{*K3)0zv|9)?QY+FJ=B>$RYwIY zvTqePr~SV1YpB20tm9qe`z3rZyTUfw{9AGbiibeukSAs}tS^2u-2vV8QFy-& z=G5t!_$iBJLeyH9gTV8Cjehkpor`UtKt(w^qDUv2-b{>J(6SA zWo!AV^8Sd~b=fLe$tpEF6TkGP*39{h@{p%{K`bgc;TKn%(~tMRJkId&!Bd!v?(l1i z?~2ABHB`-8rjMQ#tH@n|M<86wAWg9%Ag^6>tpx3mJ#V zLF4;ilDYF_f5!9>=kES6!Vel@&6Y~3?-=nB)rb6d^1dz!bl>awQ37%qFk^re4XF(S z&}qYX*8E9mQJrt9l5c-o?sRzO-mHDE)RL9a>19JB(M5y8S@xO#7$5QsHL45@vO4sgF(1<$K}H)yKz! zp)dC}=lcL0^7DXSODGPK4LEXArTTJYs_Oz7KE5Yq(tD$^94B)6RM9?D&&_Z*`SL`G z7TKhpl-{OlJ3>x|$M0^A^0h_sXS^u;3>kBXrD+7EzslO$NAB{1_sI4E-hB%c?NRRe>ev&f+3k#~8GgM_LUI z{{S;r=#7cLqDVg`-z%y2EUPGPQX>EP&N(@iZ}!P5){&2{QcZmhxlK0Gf3c9<`QJIP z7o44H8P)6JHL)QWO4E&r{;0f|I;u2fVYj8a zZsrrRBjMZ_ODLeJUxi`!liOQyE}Ec?RrfCA4})8I&dyRAoljD9Jk$jT#828n85y(>%$o=l%h|^%!nFbJ zoZyErou|--PJFu0YS8#FG^z;Wb5|bY<-H=^WL=;$a8Z#j<&3X8hu2kpAv-RK#m~%E zcAnyIXQNCP_Q%;4YonN0I_YanPg480J9@!I+zXAibi^|ms}l#jFhmmvz7+2>x30*9 zE}wDd_OICTJLv9E z;zNi%#PsOaO%~Htm*Ab;x#B-Z-{#3aCMLV9jd@1e)i?4zu1HQ~^(vdjH&VY|yqve_ z`)D(d`;mcwT*BLz#^=4v%3^%-_vUza%1jxW*oTGKe$`VCSMtYPF|6S~HSmQCC5x-`oV|dkE-HZ*ggOkrOR$1sZ@Gqo(IKemFF5dN*f5_Li9)F zs=qP3e9gSHYt9ehONF_}PZ~%sq}A(i7NP9(PxM8MhG@OCZQ7~3N=D|T7*Z1YT)GPD z@Vs|sSc8n?HNut3+TRZWs6uNzv8q1-tDxWOPSrlCl(w3IyVfbQp4Fsg@0|?&03&5H zF2Ue6hma;5#NbrYs96_W-71y09V3j?}v_!RD7rEq1}qNuI{!ON>QIh4tWFIGR>Fqvc!H zHmn(x{tHb}mQtt(j7))C1qn?alf%{7s2x6=w)9Tt*UUw7uYHw$qgkSA{hbC{D=;=i z>&w&6ih&F=H-)Zm1pHtU;lgW4YYM@_Bw>sYaK zj8FjM(SX2zB`~d7Agmcs%nCn2QTEVy^ZTK*0COpLY~%q<>=2Z zLC!p9-kU+8lp*z)g+~OC-JL8rA!-;SWT?Zsr-44^G~{o{qWJ1~be`zPOFKsHOKj-a85?x9LBIlZqE&jLuW0oIW#T43 z-*Xr&L?m7E)O$%SE4ex5mXZGIRk{a%>w{6nQid#ht0De?`W;Ftvm;ekZNj8x2_jf+ zpP+K0sy&Oa>b}v=k3nhSO`^WyxPp7gs2R+JlMD*xMQjD} zEKO3sKX!K5q-LkrG@GD1aB1-h@5!IJhC<)5B zqi8@wIIQOCvz2#GbmD~M#X7Ogkw2L75=no-=!k|)MLL8xL+BPbVE?M%a(7vTmH z1p}@?w?YlB1qt^cq4FF({mWfl$6k+bw7EFdt?V@uitZA>D^? zyfsrJ(_G9nNUZ{SyE4LuxI6dZ0(Pp*6eClyS1I{QRc>Cvy^ruilAI@#uh#vUI)X_y zpaZH%lmGQu9MLaK5`(j_ju=_*;a!{P>R`1m)g+u7_@5@1spy0Ur;j5XvZ#Bj`K zkVGD$H9EaYsXpM~1FZUmpl+U*PS{!KIVcPz*fF`QVa3r4y*h2ddP(~(K=4R&V|Y|fr^G~sbs z5DF?^CKLaBs9^3mQYVRa@1K6sWN$lbKK?K(5}I&L;iVnCy&ec zD_vPHvF?0t-}sO0>-iB0Ei$ja84%a{F>&${4ub+^2Z#3uvB4W~=ND!zie3!Zm){5N zYTUBzNceKNa|7v|hue!{mxaqzl|Z=GNm_YrY^Kbc&VU7Ai`VDul@{L_)AF*K!1g_jl9P z?L@tM2j^{J33k(4N`u}!&p$f^=f_%s)rZnA)a_Yc_$JuCW<;YUVD>%$wiOXYuBnY3 z3lEp*3_WUZ;)&x-SlbuHbLyo(18qYCa=d?^!<4#M9m3=2q9;sZdi}@{$`!@4aq;1; z-@d=nbtWFi=7;uH_*)oV<$32)=6a{LDjFe7f-XK|LnVqAlaDDsw{h;8IuuaPR^4!s z0qztstq`dKMU7$U`}=b)H(awB4Bo7`1btZ8Ek4tCbMTE51e)|mQv?2_q<%^lcFf6| zmp_iU4xyink0q<4hFg_v9DVhFzh1)OF8@i7%zJe($k>tGKU?$@LZgPv<;Vr!7IzL} zV|`J*%52DV;D7^&{r6F{eh3i)co1OrEXjO)FK3Oed}$%yax`pJm)Kq372$O%KSWKX z+a5n*g8$^J#jGnH=~?>#M%5`|IT^0Rls(9b@PBq@DxJ+g|4j5jcP@E}-q-H@mO_CQ z4m$u6KsKE!)2ep-nda~5^n0FZZ)gRxXz9Uwn*(urJd$1mazJ(jk&OmSZ9FKO96o)_ zKz5EINX}DKeB;-tA_<8v?xlU1qNh$v08K2vr-5b3?_HeYk)6g`&m&O=IUP|Ip1bTX zyI7LWhAQj-Ah4Cc347o#$a|*bvK@RsVqYsS<0}gN3=EanmJ~;5x!Wk|dME;Rq7I9K z#NSD3>)!EvCb7InVQrH7L!L?94`+UyBOwL%JgudCeeA+%wW;#wy^mrHHFxU{@s_Dj z3@G8$wh7^{jn&w68-IC-XHGc}sqx+l*LB;C&8fQ;)4fMv!UW(~s+h1LVabzA>iv&| zvQJ+r>pNFBXn_*}=(k|1pi&v7)k6BaA-2HL(oCCNsIxmhQZ%m{BX6`WBnhS} za5yp`O9VW|YCRgN)+P^d@HnlX!Eb)|#P*g17$tZQybsI!t%co~n$TVU)LnL3IC0M1 z1h;nAlU&5i{kz)5G6l3_{BWg83ma{}H+9kd3HCbs@~7?(tz(`R`&e$cGHByhmiB4) z_L-kLRBgUrKDLeJWKIh8j8;Wy=yg_mOkrt$kRta|_|wEdDnZ?0bHZ-;WXrj5_Y86LGTO zI0~8LvShr^=j6L7BZKSYo~z8| z0)2vxNj%$}#%fOiytmZ|VAh9kts8M*Hx>_<3%c7?@R3k;LAeofvJ6SV;plp+a?wwG)61p0>(%|bt?obSTnBZQ z24=eMNF8Z*xb^(m&i6S#vtQ!2BwID7xA=LS;>xtE&rU^~<#M}B48|E~>#Y27t;j3_ z?WF+74ICrim9uf1I4pLD_)USL{KnpO=EZyZrY6^V3vIMF-?^9ZsN^%X5_tY-cV3xW!&`j#6+aqs~1zED3L zQNnmZ67b${IqXXR)vo#exg6ZaMeM*8~uJa{Fb$s-a28|7WlE| z=ci0dA6euw>x@oP>xx>k4jx$)U244}LF}7Y;KN3gQP6T+YYdJ-EC<yIv-+>{ia1F53my!VXZ{=Kq#7yMUB5Q6^>vd-Am?L&t4vzactkkH)Z?IkjB25I zaDHMde{wF*@)vrH*P6cRtA1tIm-3Zg`W;Lp?#`d-t{^s75ntZ(1lr3tiP)o8FTYyP z&-@V-hbi5<8+3Afe`oVPs}BsrH3L1KHr*pDHwwxnE${5$;Gf88XuXf@i9p%3NODri zijQl&WFi|T|B_+r!yE&c0pR(|wY8@nnRRpw}Pr>v8sonLk-~S!Op&(A=fJ zM2)220ht1gM@;%~t@Qh-7W_2%Kv7h5+@bF*T_=S2$P=cq$qglj_05Sle{f1Wo0+mS zZ{8pe=j5Tjv{>X(te0$Nd+#XKi3$LwkU6FR$|ZEHw3q#(rdq@IxD7^f)yoUK%3@Cp zHbjHHZ(QTjalU@dJpeC#{D%~jzCENDCZLb%zi0%J$_FSF=WZc)`{s>Nxr9MLfe>>+ zBNB;xRE+^Lc8CLZCfAg1-(c63%~(-S$So@qXY$%ou`%{m%1G?Jk94I$NlX_CRwz9P zyk8KOMErd-Qp?@Ou*e^_<3WgD#gHzn>ZXPrD*A>o81;hEAPqvv#^+0JwDrPb2x(DG z(__gla1clq1W9EA(LBVph+70kH6)e|fESwa;53wuf$o$OB#QtRi|NI5laci5ui^B7 zwrPcnUv+CIDi2UP+gMQRr8MMR+G7mllgNS+k8|QAB;*I%E?|b`EY2dt>80#-2%G-J zOr!u9G_HYGb(dY7Y#?N?HxdY4X_IuhfXn|Xf|g=lFa+7L)_l-lun>U#7Z`3cq6Hy`iOj#D!TMjszRk0T`f!S49he~_ zPyhW%`ghX_y5DeeqQn-7_x?XeDVq`RiX3&vwl4--1K}&xf8qg^nSXH?(o;psc_70l zdo>3Rj#)cpvCIyLI*bynnY!KZ>ikmqj%R!dQhkB-Oz{67^xxZp8A&RzieM6vq!vPB zh6)`?deAR}vKM$B7DF-j0^@S4r7mxqm3g9}>jiIIDrHrJ6_kxZkf9lc4G$|;B+#cr zkOFFufRgPhQd;@%ktKjP9DdDh?ML@TFhhavR3AHOS_?g^nPfzLDAjn<+73|O@q`*<9fOu(ix)8{`*rUY;?(YL3gcpJZ;5MyI3l5G^?#|vn^FL0{|j}{R008}WZ~c4kJ53uBiv2k$@-zBwDV>JW9tJQ+LpA1va234J4Z=-s6r7o zaj1gc%1@W(f*ul@EJ z*3-BCSdtEGm}Huhpt{I%hYz693T25N9BSz39=sp4LI_p|g$Xwv{!xtJ8N_C+f_$^5 zp1l@!oj$8gx7C>{lJKCDMj-o*O3tJh>;eNw7bzhq^py^pFd-vWndq@o94R%yv;H;T z$VWjP6_o&ztpH7DB3E5B=Mp!w$jg^#Yu@mA%iE!5V@&kPlM(kZ>pxJ|$XE!h`>=ON zdsjG;?nSa6yJJreXrfz!KpH^Z5D9yNjP;9r<*@;R7BKDb7hvtQc>NBaYy_15p9X~t zKVPkXt!{lXvCKmn(O>)yAnW<3sE7b#A*w_uc2RMH1urOV0_T+-MTYcSKL@a5#_$}qTSL~3AJltT_M-J_4H8Bf7C z!AMDp50Ldts79jRMY&+O=~*z9*6-2kl+mve60$f#e6lQ&%jNRcyDZTl+$3h;2Qc<| zBY_pdaIF_|`O)DY7vXOxam&CZMBL!2M$v>7$OIa!!weE^MFZEueakeFPD#q50GowL zws;dca7e0jKI)lhFz!OCBkUkUb|8`Tl23`@Z}5n4uY{jKydOK_0FkC$G)*0VRHfk2 z!3gViu=0NHj2Jx}Yg1=lx)DlH6Vh>Ys=6wc@3+DTZ6k<1iA7{*`^9{@n++7=5m zh#`4qGFX~{3}nXb`D?Q?o4eIcrGJS^?~S+J`M$S{LUdh2U{z@}s#>!j=Z`^!jU-bb zb!bW&EfgSj5!F96)c?W1C}gimL|q^PBR*{_bM$%9U0U6gJ72zyqjp>(*DSQNM$1cahk2xxBx71WStL|#bUkxUcMnvv4^b;DIBD96o7 ztN(#>@Y?dF9!6UyZ*sKxHv_!m|%`9rP`lwCORRT?)KWHpPoI9l9h4~IU z-K9e-MbMEtOwYLu!I4Zmx?&MDeeb2mj4xREu<^#KX|y!;U+=m!ACh`CAJfjXSX`K> zeO}L!@)XA5WKPOAtSX|#B*zW9hNgR9CErMB{kB~P)><@&_*j@`t-bLFrP6s8`gHsf z4Rh~JmucP5mN}NwY2VLwD*HPYCLS{#@W>Em?}&8%aC;TT3l81;sMF_Hu3z5%LGcL4@6 zL}C%ikY~?~Qb02j&=3SG4Qtp+VJ?PLM>w&Izb1x$f<>p3vQ}4gLC90AZ7zzQZC>6Y zV@WL;GlX#r&H_5yhYj|}7rHqCsjQ$#1*vsVa~if& za_PF8YN^>MZh3eu%_Y7O6P!!IUKo?V#EsGEkFBn;Nt2<27SQ-qXtG1_9O8zNi<7bi z9ear4LGXLXw#5K(2p_@HOd|N3`3hiO2B_T6@b9Bcl(wc~)SDfTe>`QPmf` zL!IwrNUktN$c zDQH?qPc#12bAP(Obu%P9`VVzM?S0#&vKSdRm2!18Dsf}V-OUvp9lVw{SJiDiDl#}Y zXp@7&PTHik*Xn+}RP>-J>7gP1DICt&g_Aa)K!Z*X*f6p`yY-3WT>nR0P&}MKm48Ay z$J>;;wnbp$>dm`VSC+@)*=|^HEYVI~U@p`1IU^{4Am5EY|2(oa24{ula}Xd%htEI{ z803(Ge}WO50tEyt|1$gsun{A=W$E}NO~Zqy-~ zRrx#Kx&&6EOzy79bb(_wM-=BJw~jnF092Hq8N9cplaxFcZ!)Dnw7*p8j(WI8NU;6o^Q5MyvOhNwVrTs=K? z0b_K3-_D*&{({{PPXzjhjg$eRzQmQAv9F23iewp*_94|Zq%I5||9-~_p@vE2+ppc0 zJbsV9NMC8S;~1f-$d%1C*K@hMrJUxtN$ei*bL!M$I@nG$vwJkL_sJf?lJxNsUxVDR zCL(UK#fbf}lyUbCsk(979Vp&SOq#aX6%ZSR3?}kj`3W0k86rV}?{22aa9AP6 z84Z(JE|Y7IHY*NC?-QT5H)--H*Jx_A^{v!ZRHM`T5wdY%tAsElN+U=6q^aVeythUF z0j`VR5|r)tOzf*U{xQhQV6ULmLS@0m74eTW_OiE)w}SOPF3O)4y7^t6wQy!*LT}>+ z;Wdrr=$>6Lv8KtD%b{31^@s6eoRp{00004XF*Lt006O% z3;baP0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00006 zVoOIv0RI600RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru ziIkaSG-`%w8LNx@&$QSWX)w+ zk=oVkIB4tS`*~w&Gz5K@l5u%Zu{EZ8HsLt02?9GKqJ`XA29)nGQenIV6u$?&2GtSyYT81UirSm+;Q_@ zR0N>ndp73$-<15#%=RLLDbthrDzePJs~$%Se>;Ix4PX->)ao;DZ01sz4vqbS%2rf4D4d^;3uS0an5KrsTTJXl~nO z`mM3AD5y-@4|oK;3%uvw9|I4W1*J^&ma&{7 zSmb5Zb^H3)`*nJ}3c#$|c4nLz7!^PX>~l7aq&c;oD2PDN{LUMhl67$MvhN%QOhYDY zDq~RR7zn)rUijBblvgMxn7XeIB%Jl~YQ%&IP{QHc!Jgh8K$=K^fyf>PI*%~`I%Jj+ z9WreT$}^#i0WAWd&6Rnh)?&k|@3=Y`EwVXf0F$>J<;Rq@OFSqMfI7cKP`l$wvkqG?!%x`-Iy!LH+9e`Nr*gE5~)s?og0cwDfbe*lY>Ir zK}QXg8foPEMN8hxUNOds&M^Rb>EE9N-vQqOKcM;o=%O>~#OdYL{oUI>@3qOB&X6LE z**w71ZI3X}`H%?+J;VSg?;Y1++G*g;63zcn!ZgNG!=rA|F;;PuC~GSfZOT9|V@eu; znfte6 z!j!6xmw{lY<_6w&_KI}&Sa1GaZe%khV>x*b!0hi4224Ky-=cgC`~mnQ@Dw;fwTEhg zNWMRl2Ya`DZY#^1!jLlWxDFGCg!eEelz)AIv7-B!O0JoXN@WeqXqC2$JnaN>Kfw9> zIDZ~Pj46THm7*L|LK?eLBex})>OUy?PBF)+L*Oy!2Pi+n!00LP1bBwXE6iiBkFlmZ z|1`fTfD~dk@Ewc={S^2a@DmJ}_Avm;yJR|Ky%H0B_*-Q!B z*iCzkspdY$0O7D)L7bAot^*`7{#ec3}v5dZW)x;rYoy9G!S$!tXLV=ITBV~prC%t;}0 z)!v~2qndJ!-gN8s#LJW&^B9uK>ntSVzj zGUo)!*i)Kq4|2{7YQeM3a;1^GBO#NxNU%ltM_BOweUyFRcbE(L5pcoLWgjO>n2YUr zuz%a{Ra3u&?gX#?t&&@^8jg@5rvoo!WPDgz6Abl;LkBN z-G>;3-MsU-i*i%xfR*wy=_=J=RGJrByg!m>N_elkFt<|!N{!qd5nvl(8(Xk{ zhS||SKzRtXacn{Qnv@>qcbf3>!XEf{Hj623@(j_hOPvf1t7LST`CKC&Dj!x@EGn?D|J>&^fKi@lrIX;<@yhe6MWPyMMu60pdYve9k((Pa}8qaw7cTNJ3N0<%%G4Ls-!u$WA{2eMM`0S57JDcqF{>`za+hQI< ziV(1k@Gh2f?k|9Uf_a3zhk;SE4!upe?sP!QFr!MfTY)9jwIP-D=kiP`*Dmw{v|~_e zz^IWsDFWI8$znElfR@(?9D@7?xsqVID!02}5 zDr{$&wpV3Fc@Z_Ggewb*E!kGZocDx@MWw$eSVow(xIbwxlhr4pV@#1AK$UDrYHX_zUU(i zi2eg685eeG>_wX=o5+mHn9WSBq#9sUHD1YrXEU3Uk6G#@mTD+FOq0=}k-INhR`Cxo z8~$U=)42uS*}+#Ay9v2oK$>Mn2Uu#0Ut%uZ{{#c4tWs+uw|=9ug1LkTpbp0|kn-fzfa#-q*EW!wVn`9@ za`+hrME^dZF3P6}{}cnGKf|)pHqHy1AXB;A=CqmjxmMe2Vn%t%$0XJ1 zuarGiRiYJ3Gh|U3h*Rh|wCyG}l@?vcMjF|C$$mZiz|Z{i+yPQV1R2LFU5l(26fSzX zb4mmWcQ6~#pJHmde@X*JcT27VjOy$=Z~MGdKmqeju*-l^f%T{pBr%t8NfdRdfl*6K z`f8++J1r45ckg4rfD5e5!p3H_2~sC{^>u*Jy47?=yUp1By9AWx z#UCvKkb9BX5QXldzl!I0ky4-=lhti1NqdN$bxZT&91!I;rw;HRMf6dga+U(-+eY^^VR^y(ln z2}Tr#Lm>?J5cvu4zhOGgsF95msjoiU1lR6GZN@c{=ENjTfzlGHz2a^m-~uETB(>|P zDabBpC!J2fx(MhSY24c2U@S+MjE*rvWw-s8vx&; zTmXLu@)XsS&+fzb=hpJ$)e8QtQ;im4{_Fn%rmFjxyPOVap7H!m5G}m2Z6(zi7G9Uz zWJYCD=Kd|k26K+5HNI1y?k>U{N{0cG7$}hfy9v7GrfVrxUE@X?xjPb}yo=580Wd`M z2(-tyr)%hZRhgs(%6^vbV0DcC3bPwM;I4wvJb9BQlrm5%eqW~#sCQA;&5Wv~SR-lX z%O%HFrS==E?f?~J6o5wFr$G@>w579gQ4L#a_I7w%%{NtyD8f7jt&hVHS%Ui3roQI8CKo<48wunbK`)Nxp}84 z?C!!~u#4G>{t63#{Q%2@^mfQyW=vO^Aa4td>JBb*YPiZdF9M_Nqt_9L17bm9k5V~s zoMK9iG;+5k^SpZm`~v7>fwM0;msjkm@b!^B{a-v>s^irel1NCew~fgAi2M>OC-EhB z8I0;qenp1#rhwAC3A}XM27%Fx>v!I@ZI1i*qI0Zxm*Re*Y3m3P8E$OUL$u)wt&yEr~D=GjD2-jald$#4(kI_mN0evih7kj zIuVr2dv`|4Cfh~lu<2lwA6{8ryLG7PO17zhgqU|-imC2mUq>mcJ{u_7PW?W)IH^Us z_Zn$rGbLk50q_G>X?6&FO$JE6du=U$@~Bpa^#N&r+ukS63|pAJ=%4uaL+;*`Y;x7f zuPn}F)33xlXNIbC-2^aNrft!=VI69^yymTNzb!sV43rX(%=?bW%yp`e#VO^}3^hR1 z$laD5&@V77=mO7Cy#PR#OU|=AL)r%ZQQ#B3k2x~@dl+rIn{|y=QPWj@Ut}0}0hHXF z?@B-40vIhDteZJA6qBvy@6RgCNGBzbO2^>6?lcl4@^MfJh|hf&q!v8u?kO6ilMWg{ zYUFN93yV)u;05saAg90x)upPx5ala`pJHJ2kh|#Oz0QfCsP@`)wlro) zHL^8a#kKpUz-UTxs<5)SDw)$y9cEOvZ$@|mQ{8EpTO6i>3??q&5@bv%U%FxwACg8I zsU-oXA^QmU3e{g=b(Ed}m&` zMl0=G7-z`GeNWA(ls?502|1e{Kol8Xpb(L`_uF}j^jdq^~Fsgb)U*-vB- z_z|YM8)423YX+oDRVP^9g-h-MKSTHnEE~htZC{OBm1QPfxP4%n3Wlu!rJ3<>Fc?iq zi8@j8)MklTq&W@@3js|)y)8+y1pQg?Y(K}8y#8;hbVVbL++As5VLyKgyaxUec!`qm z+gH}&%g1v&sLzl_2#z)ZkAXk&2Yr{jR8nGX6JL!si;V5JA!FnI0w9_i{|f1Vt|RDm zVcW8C=HijcY)5sNQC%>avR~nYWd#k@(miIKr64^kg&sK2)H9`>)P6}#@N6S@R{~69 z_7&!J_kn-EK2b>Sgwg>y-SlGiQb>P`azk zsH|<(b-oT}lx@pAx^On$#PA6n-q>;NN`BD|1qI`uhr9S{C zbvmdINWy*KmneUM@Bw!zHPEIoql}$Q0h8V=FlAtry(*3T8MyA+}|q9 zsA`+HB@vm|9jRKNY}U|urj%w(3EW}gnUYSPl|~x5dlCTeq5Rue7Kd}L1W4z(DzGBp zT!_eHEco?Lv0$jX=T)~3xe6HN|1PfFZ*uM_vZEQ#zzq+29ZL;$Vo9^oeO6^gHB@(+ zWdx6Pa;+WaeU}!PQsB_GPEfG{rAF?a2$o#?7Z?!zJ*s~s2x9*J<)qc0yfd@oVup0u z6%Mv{fcJb!@-MN(dYdmd$&426Q#%9eDEnLn_;Wl9 zugr`}-p6_F!W=oy_mP711H?t3iaOAP1h?aZXXmL_8);;7Wgm-8dWPAO?&I7EmLa_I zXM@wE)@`f=ZEqstU`z`}Cb=u_b ztsp{_vV3hwAX#~;|G_xgTYP!thF_kH%1d2PrY)WIMl-;x3U>cli zWFuw#tR7%mwWnB2(m8WVR7`_QW;;^NKq)}RZr(q}0-rWt>Vna_92gdXQCZtca1vWd z2Y%x^C!DdmPNpZgWzKb7ZR>(jnVN1D=9PE7F4RV3*Y^0GJlktPOkGX*#0e|$b6jk^)&#lETCONsQ*-jKfoOBR* z54l5tw9X&sstyd}HcPJNi|RCyNotv_t5r)Bg6A#+14NfqJGCfJ?Uj^7^r1Y{Hx46 zRq;pp@7E1Rs{qb24h(hs%Y%49Em>wxS}QXu?w_gcBF6SYDYNGZ5C_P)5TrhZcmnRB zqG4KInq8N9_#0{DMr8{tOY|A$hwzA~GXMrG1fXkEkSmGuB8Ww<{eCOMVQzMmfS< zz^9}NkU}i@HA@lnb|g^U1ZGq8IE>Fy{tZAVX$VcG*r@@N6tdH#9AuQy|7Av85S`1fmFn$o6q=qQ1+k z02^uK_N9ZV?0$?@l>OcKn}7Yn-v9L7qRMXkSAYT1k1;^nyfi^?Iy0JT=ljn9C0l6+ zc=Zj_wxY7*c!N2qtPfxowY@H8w2Y%fzRPO>BPr{r*dPZun#CoxOnoOA@QnA@DKHUH z%YE02kR(C+F&tmw4uv5`h@Px1`uUjZ$PzjTmJlKvPV zU3hozB!N~7RJVb5FhkV4+_ic~%gUxPqpJ4#Rj>2T4w-#v9$U&(b~gz|b*@vXtWix@ z_T3^!hO%u{_o&EV=N&)yy@rfE?wGPAl^K%&6(kmjL!=)NoF{O6jN7|JA05J@cVTA_ z!U%%UGbzmh6Ad;sa@*3vNY2Mtp1VV0cS$JDKd?YiWfz12N!lWihlqTDQLKk-QhD;X zff-FrjwKUxrf$oETvL2z=^R@s^RCOM44P-)U-r6eA~Py=Y{(sBkpsdao6)>BWqlRZ zhu*nCBdlgp9z3$0bn>^6r+^^cgFzSl?iJGi4LoR}pMD6Rd>@-CF*m*U{LY)Fu3MK}35-g%T_+e7C(Z10 z>H}$=sdk8T5?DLV0zobqmg@y0me_Yn*2s+NsPE>TOWpUXZN=?n&JDo?kO*|*+)x~w z<`FOewvl+3;7H;5G49D3`uYbrKLHKky^o;NF{UKq>!>S@Yj`8KEZLL2k73KlSg>rL z89-3sc!`78uq2OB@{4WP${z$ta#t^;+MtG86a&|iHUXoj_x`b z6(!Tkq%v`MP$r356Xs<1Uh%qI6(q)TlVFtJD7OYi*;q8fT)-nEGE;%FV=sstC}n!W z))w;k$4C?rTs%j=`5)lPAJC`I;OY0!yM(V2c>hD#KlFX%>u@!o)X1$#gqeXp!949I z81z5>kbM|GsUdJ5UDDz_3AJG;gBIJgc(518nli)OG8+E^7mcH9IiW zXEvGdi|>e(Ah%{KR_ORMey+FxR?JLo<2kS7trs) z3(uB5`P`fy_Vz7yDNVqoIT!I70n&{$vOWngYH2-zx}vHZ~HuO%K~-PoEjv- zY9pnY>aGlo{JScIArb~i)PiUmO$X@7ckucNJbwl+Ul>zzDQs;UAVoRfq;YnK28|lI zQORbf0q_m*8!QVr{HOnn2n8aq+lh4x;Q?kpI%HC%=k|tv6Me0+>RtyJO|@Nt8P%~Z zWl}c?cHVcJ=)ka!wsja%)ize#H#=@t=DdYfl#Q8HsZH&h1FE2n?0rD+UW|5Mp|5@m zmq+OF35aLJKKK~!KQMrb+IE~Y$=Q|~aBAe*CBPgT9s>_S_lQ$L1j0Z= z{>#6C%S7m=0iEz&gnt73mER$k3i1YJ-ILEs%qaWghF!ZCflTG(S2CF_o2zc#cXyQ; z<>%QvLrNFhkd|gfb?Q#V3P}&*9t^v1aSo@) zaB>7^r`Ff@t^rcJV;LSY)m>v&YUE}_u((zSd<*=RAUGqC6mStDupNe2dZ7FMbr0C) zO?XV-BxbY-jMfF@ZwfQ2V^1tHq_Tb24MrK@W#5H@wiJm34HR(;`T_$I0g2ks*(bR7 z3B3OoFucU|&(PyDV@EHa!EgxOE({0I@52{g!3Uqf!97bo6h)9aAG37#fAWoNv}BL{ z4u&(2fdfo8kc9l|*MPJUd4TW*!oPzRhkmdo^eRc4e)F1AN97x=OQg;^U)m{Ha77O7QB+RpT!Wn)Mbi7`MbV=Z|* z(zp*(V2DIWYZuZOJ^L2zz69rUSH>e#+a14#7tf%30byt?X=fKY+tw}vq_N7dQQXUaY!u;t=_@VYI8C(tSTGNjOOgeg3nb|XxDvRuG4OFo&D9_x5{*~ z=(<*^&+4=-`%ds>DFltwvVa$Zn2TVPeRioiCbagEd!G|LdWC!VCBfwj^x`=>>cVIQ z@d;drq^5T=2PTP=Vris+P54rc6P7am*XZ0KtV{nqH zF*#T0cnn6xV=Dw961KplNa`ILhG*#DWP1N3fxc%;r^g0L&RH(Jy#okaxxSmULycUU zIE)pY055>2z-0zHSplVYF>t!OTzw^Gbj_~aRb$u`X0%CduZtNi17_C%Ms@nv5Gzd) z`O*|wn_NuNETCoX+X_K|>^wr={WF45550H_7f&JXPY#kM=HzgCY_H1;Q|D#w-+Kp^ zq}6AIu;VstsgbpdU=_&U!Tc6l0Fj@ITYlp^+$11v3^S@|yH;jYIM%wE(N(WqRokVR z(G~i=4ra8dKdmSsQOALSIc;+93lp(se|xo^go27rvK-BTkrv(o=j>x^7rFlh!SDk2 z^6!z;Z_(}xNJi6)N7Y=xzy1vj`fz>*&whX}e-0mgX11juGL>Kw8(S-_``$<+D@lMk zHax)0L=jBXauF7H^bkX>yZ6-HWiVpF=QoWR-6R;5*@&|3R{^7SFrz{n4JX!emoH{jaQ&Wn;9Z`Z3h&i(i(d)?c=rR# zg%{-NnvB`f-J2d7*+|*KaA%JaK-$HS>gK)c7RjQ??=FMUI+@YBZA43fL`|E~GHuoY zM%nxLG77Rxa@G9yWx+_Pj$f zUm*8?j2!+3T#Ak^XPHu#A8*))(ZJ%C!pPXr#ktv)-uVD__bhfPNerx}2D2G!*hcP@ zWPr2_Y@@mraolUu4mt>1m`=~T*xZ0DDwH-)R$>4f%#5atAK6zDEqsvUF^CT$8E}WF{5RVQ)Q!H1kBgLjPep-YP(Rp zH@431Q_SR0%myA<*Ma3X>;!?R`pji$A+1)X<@&D+71jXk{AdG z4nKuth`V@-9Q_6zo_io!5crxV@ZzbtgH-p%E}yDN*LMm9x4 z40rZ`gUJ7}i&3ZBwI5ZL)y`xS!Ki8sb-<`}dyU+5`CMb;YgY3D+?0)H-Thq!jEXF% zCNM1Q|ElV_vhT97hCn0+NLeZRtkFC$(%g2J0D^2iB)In@g8M(l9ejd}F46u8q@xAb zQ4Nd+o+%+_Ka$V@Z~xHBScnw7?;2^OwghC}cFJLdG*1xg!jGm`le zP1!i7+ANs`JFk`zUvNKUeRL5Z<>l-Ie)o4U=vnHavt#(_pTUQpngfKA zQ{QQ$x@%+^$=tukfRuUH-R@&lwu&wOU6f5`Mzz{3;Z2vh+`81SNPT*jAb=pWcc(0O zQLcC`ue~LuIq~XxmQPhHa59~b%ENN?=#Wm8%Vrk*yU?Y?Rgk3fP zkTS_Lrj@{GL%iu0PDV?3(@B;JF};21E8PBMHKGJsEyrO=@&>b>e@=UswFzd$dZ z;a>ke+CMh6-7-m3`(60q+sUHn`~-gb=kVzlzKlg|9(ZZmFr`N7N(M-tAt}4Q1jXja zs`jK!4tkve*sHj9s~M~>&!E?X{R3#VV0*{<4RXiIk5RJETJJdXu1}rqS82O$W|Z&u zieNPNyQ_dv@%Z8cWTTv|u&hv15sb8O&@{F`JBI{^pQHD`AUOCK?H!^0BUt2hmzQ`1 zC$BBV(4cQl4iZ=;+DGr0_npXu%{NS`k;Rf_3fj-B>~5~yL|s`{g|{hSwD4FffKgse z)9%rxTOe)kOsdfa!E)-rM&3rr zGC1x4dm+F2^?!))0QdweKe5?M8L9cjI>BhF?KP(Zs>3V-?+lsg@h?u{>Gw84v^&u1 zmApR$4w|dPsL9Tb1u+rfl=K>L7n52Wn!3j{coBXRqYR`Shfb0Nj&sS>4_P4|3YUO zK&6QV-9|eE`yZo+pW}AlN4CC02d9hUlFGz6Q{NrEgx~zn#?reNwjhdHu(fR-pbBGe zo2EQ^BhwOLaj?4?Rq9}&c$>L&nK9_HlJ5U%ZEK_hns)+=+SWZEn3T3|ci`|oJbuq! z&z`_P{++2$aPZ?0w)deGL5g?DT{Lc)ye%_tTo-WFy-s!8YT;FNotI=eEALaaEt9}f z2u;dSLo_eI<1wV7x0shT_%v7_St z%PtHnCogYHQf&44-be7Ke`TxHfBE0QcVFA~s12WdZgC=6j7ZfOv(F;cuHlQuS@n6D zw%5VHCMJ|>ASzz(qAKl%nbFh&EUSOuuK(fC?3us&2N=bcDO^G;U9q)owxtvx&DpgYX@n39j_ri}>ev5UU?1TzaF0!Y zN@T%?n_@n-G#1Ivp!V9Y*G!E5V$8GhiyX ziq{!uo%389Gm<6R=EQ--1shV?zB6{3n(sB+R`y#lesOBGkrG8xdyNNJ=b0?nlSD#9 zat;S^554#f?UhuOT}sqq#*=%GXn(T&3E`+a`UW3nU?b^I!c( zi0lE6u!^yp|9D9Ly@p*UFH13_6>LW1_XW&onK4!!SFs;=D}t@Isbq$+l@Iv#_wfB6 z4S@FU!OpgURur1rN3-+Gzw-Up%zCiwIjyvBVVuhzCp(qY07+u;7Xafrz(|$> zqujW~DP&+2J0x+l`;@V0n6RcPkj?49gBF_f(6euF-Dj)Wk!J3JGj-i?0GAi`zIR~i zeN!Op&UK>g@T*gdpW>Nr2zyyM99EGjj|wCGwcdrg)NdR=r) zGtW9LY}W<*&3nIU+p_K1-`S$erx7a6`#3Z90vP4H0&h!lAQ94efb4#N?0-bC`yT32 z%XN1Z#{=gKgkHa}_@pF;y+h07@a~89JO+WUZp-q&kw)G$3GnHJL;lOZ`Hz7egnL*_ z(%o6E6q(XILws#!#A0SN*Jf2CTX&T#bu7sp&$FGV1$%q8>G-uTu=d&}2E-moPdKagbyJdhe zq%`DLfAfC;b`Uwl(l^{4Kq5C#n-YIrS`9at%zxL-Av4qgh+&g>bb=T@l?3!5;*gH1$G_p1sVrsiIN}}eRWHMg z@&F@2F0ij+zpU-t@2Q@UR{^84z5*ZBub77&Vi}sB|6rLMy57*dy=TC*)rNM9?0O^LVsq)ISMkx@a_n*Wdk9Vr8&nSRZd zBO>5p^x}K;@`(Y|x~2kk6V;t__G)(wm^vNCjud&@S2nzcIlT>%0a6kn60ylKNOdJk zD4MV6lr8dB0i)uNW6>mC)~@PU%Ph9GcEqMLY8iQ$P_FXd=c@gz$y1Wf((Y?5CFsjAavl9?BuvanL z_sr+2z}cxb*u_X=V@lia66}A9?7nBuPuydw#@1EDc|baT4gdJJmSQN3;P3&2@A-2G zFn93$`CR?bjob+ld@0J1U;X<3%%p_uHg^@6^2^*ZmbC1OJkztsv9XFEG`ma25XwN} zHo+)Ah9EFoQ7FECO>FGw^vE0@h66jNcE@!2?Uud6#L+=Yj-CCT0hH!0detPd6jNNY znl2~e1mcO?_jr5Ny|@i9nrEAo97BqodERO6OLCtxo0T$u+>-|eqAkymo}j(wko1=? zO|eKaU>XfACMjweli%61XL0Oxm%o^Ib2@5dDH&l5$wgS&hP%&@@;^LL&y*^_qKp}p zwM!GT#kkZW&x5>)t!BXMO@a~G4>~Z_-N6C;$)8&)q3#8|dJ0d!g`Q9E;?!(QU;YU^ z%;z1``N!7WDm_r!uWWGFt)^SlCd11-GxG0Jwi4e2Fq(JGGVjPBH}D*jmzk^$THSx1 zO$C7|N?QBK-iHJSpQFPwbaaX3om!JPXQ_pbUchhv)^AHaAZ^W-t;lDhXyi?oF+=*l zU~b<#m<#yb1}2zhN>$n=%{OHBhc5zgV$tRo=T>)UIJC>w>DUS{?>2oKHlwQkgh_ex z&X%o2FHS9rzIzG%OJhlyPQTSQhSX}=OM(dtnm69;8s^&;E^94Whii9Po2*-(R8?Yu zSS%hX`+kjJG-f6>+gA2lajxPr8))HYk~unz2Bvw~+4XF_R`1kO(2_y}IVvcIDc4i6^z;TG8=sK*?q5x~$sDn6`9e(ds1VJ^NyF@ znz72x8;K^W$1;`aA~4D^CQSfAwjO%th7XX=J#=)zIFNQ-lG%#77x3&mV?|L5_V4)& z;X4qvd~v^mqeCNaqD(n8{AXCjK1;ItrpnmGB7NyX$vu$V)PuRYT@-`(mn~rCy-;!-J zZLY|SrjDV$GUopwzxwrm z&ZIz6`?g1R?UF%nK3=JeEh*ls$v2F=TH%N9;BWtx6(kD++jg)EyW7@jRh-MUgOO0~ zzMh@8j7c%=Ed!ue&x{?Z8kqJjt(H>a-Q2g@aIgoR7UV0e=CpA$X=!rD70;R`oGJ6- zn{zGZeOK33muF_`9U8I>4RzFZW!qM<88N5d1>a5epF5Y#b~H{QRCz91c(1-*l4VK| z@y-nn9iGAH1RWeh|FsX4y+%n>d;XHx*wOZ$IS;kG_g&i9l-^*8{mVu8Y3jB|bR&{y zN?FYx^}qDApmxjbFo`p^@X@CRK)?G3Q+s{=J4+UscrP_+*+f4sFV5^_Q2aapd7aE? zrmdPSNCJ5H9{l_-On?8~@8SH^Jn%+CbI0xXe2}8IQ*^fB!F!;7wd&8SUO8_M0D(?; zc-<%MDmJ6Cc6rzDMc1?j7}afC{~pcRFE2how)8qK1fyA%o)kUq)EKpFJs{Zsl;G?S zxbOznse_&cZwKB!yR~D_+dCiGwy9XFM&2+PyPk#o>ev4aV@Ms$mh@&ze%U!zROXh{ zQ!F6II@&FW0*g!P4{Snz`n@rwG__US-j1op!eG+JI>2adA0-Bwc@tf$1)a7rq`}Zs zCf##eoq|nTqk(OUN7jE7!tM@icg#kVKeu^f9>2V8$-l<;A^)m6->T~}@3;jOmc)V# zd{WiA>~$3|TC{y$n>_2Ba~;H4U8G=S?%y-z8!9Wv&I6mhU$Ww%sgko8xvAjUM&1xfC}Bu%hjKRmEN5puc2!cFJX07u2RJtgMBLtnFMnn= zhmr*ToBt=gerbOX20or=$8_M}_1}KdxGES`P0EWV!~BG#7+7v?o9*kDf90!uzBE04 z;?L{o#YBA)C$_D<3-4}QVc~IZKdnA?{_o6wBgT{@6coK4Fv=uTak;{41&~+@j24aY zs{LodvHARpGpU1$ebfM>tcgpE0k`%Ec0M53eIM!EgVwfX?p{mM)j87Znr$;p;r=^j z$9(@2voA@=;+xpW9To}s)vy0K#*o^)4Gd}O^eK3P$%HW#?Mhjt)y|f$&U$IyYGyV-^Iot$Z}3kr`_a&Q*j4Sb4mI8Qoac;B ztM^;zt*71+H%_vemZ<3}K2M#C&S~R61Z_kDwEr5{{Q*W7XgV~&x=vBG1yH7SP&M`4 zfiGF%au=t_h8wx7lK7YN$=Tjg8Bfv}BnLXZ`p-|Mu@CRx-Gh%lhM)feo<1>S>o>oH zzx$so#s0~WIR$if;NCs5iQL?$^DEUQY(_KNrDU?IcrUg|ygez}f%iUwVc)WWce^$T z48QepRnF=u4f^JhHyGIJHS9op&wD`me#?^B<^gkR`^4>9QuT?e1PygY)mHy{RBU(V z@-3+t0Os%ab=i#SGTPkVLXG}&A0@~%wHd3g0wbODU)R2*>YW=xG~7nE9~10-g7#h@ zqYL!90%eQR@50xAXN#h(ZFumGIS`Ru0Dl1+vh*g%_$LtZt6%>Yn8~?~O%HFMNXctl z@XueXEb~DtoG>2=P386SDIC8tmNDr2DD%{QIGruCKLtT9C6DiGC7V$;;F31AY!@<7 z-L*3HoBx4l*&E`~B#&5{*ylmt*i#T9L1601&h|u|Ig`~vW+&k6eaf~}_`9xMY0>>u zEJe`Bd)Osj67l{ukM$5O5QQB<89)kX0Lf$=qD8?2kLehi zcE^HdRW0#qzi%LPdSv#be$Re1nY3Cqc?Y3)V`Z`0t@zzK!6^G2LTiI(r{qvhO=Y)JQ`V`-_8!~ zbWDYpd7fo5Z&G8;%qagZVs(>(!0cYFEmPgSd}2%~FD^BkQ}5DZmwJ~-XU8UqcFr}| zm5iy30cb9`*2(yV%K=lB3a{#1$79VoGsJ~!z2F*NIZ^7e%&6@8P|>e=KpHs%q%3_< zF@;ce4t1GP>A5IbRKYKt5hCz8rpQ|vVc~Yb8LT|dV^UATij3Jo~sW2hu z*xzE?(SmWS8Vi;X21mw_hR4R5ZW5H#c~4I!36BpR*g~+(d$p0fCMh|U-Cxr%q*;-| z`xuO5!fqV?2h#EOA^+pJyfD>WlGp^^y)?U0@6xgux3?_S(2jSXuFQWohZ$9U&Tm@; zWZB=D!$zlVL9j`TB&ji^^Aq!Qn(D__{v7r#fcEm-Ca(SaJ_lLmS;#C~Do&PVW6uC3 zo!FNsvZXTnQf`~1o*j*37LcyOj22zbIa0}t7TwpvIf`OnG)f`Ks>kXSP_G6?(`^Oy z?%yFgJVkpy!06PUy{^D-H+i*2e(J5;a<^B}Z3 zP!dES5z@VYqvz1SDD0!goQ8d~HC>)V5FuN86I)r>qAEsd%C=EvS7LIVGk5JNP^yyb zyVTo{yvlAWyZ1VnQ59Isy&r3GkPy3H!6-$B&N6@JZAh#MjI#Hjg-C?P=V<>0Iy^y# zCpYJ9mmg?6G(ZYN*gc$h-*vXFNyZc#Thd*V29Rc?xbn`xClMHlRT_k`pw~qPePcnx-fTzC>-R5Ct&D|JWarRUty$rI z7=Nbr^8v1T0G%HEq(6y0Ga8k+d(TrHF2h`^u5k(A%mt%LP_B3vtS-_hH9J!52(nDr z#gtB00VB^yGxu*z2Il@fJVr0SLkF+9HAxfuEd?PweAnzrVZ^v_R8#)^uE`Wb`hQ>y z>FoieDqBPVOF^{0#)#tFD+{PBPMmqkCW9cbA4zA+Cggt4GB*qd22xpGyEHX7@$QAO zrZh2C*WMnq!^yz%2HSaRqACZ58h_8I&k%^b2Vodl4zqX!$Is1PRHmm_^-O8#*-{sY zM-z{zb_)spdF9=&%fP5?LeEro%ESvV$$8}E8IyWuG+69ix7G{{#dlrcO;_hWRUFf7 zNNx;B=4n?oCY{rNUQH*Z$yO!v=y5TmLv-*Ocm6fnf6lFmOMU1;=*v|21=P--{RUGX zLszB-Z)6i?3`lPW02QV9;~aw#9)y@uGtC&z%O)kKFmxboc>gnKZ(Cf_tLLV!d;SCT zy>-2JX$d6HPjcB2Qt0f$y$6ib4e1XQWB>EcD#|#Lh_ulsW;-f&s@V1?zlV!s^!(Vbu0!Pgk74(o_bOx>h^o|#MRt*GPdw`ptj1&utK`+f z(hud^tQ(A~o}pq}Eg5&=yp{%|ybY;pVKmdnEWj+X85IFcF?ApkA?-t?bKlZV1=02A zwW|{6jIAF%hp+$E0BQHYm{JflKy-&CZ$o-B8B!G+%G-#N92-iQE=IRVa{6|hUzK%y zd=eOA8pRfqH0Uvo8F9{lYA~?v@yLSz5VPO3JCh&RJZ1*ea(*aTWl#4}vR^AJ=9eOI zY~#B;HJ9(wYoY#}vhx`s$q1qr((ZVNhiDS3R0gJnNxQ1}U*5hneorUa8DvyoV})~F z=z3j;nU=M^4mDk_P39^u1sjs3(aD2)9cJYFEK})GI3@xTpraG?{P$@81-KFQek9i< zs`gBZz`ci)%psEfZnR-acSA~@8s0oWDvMXi*ib6IPGKI5{Pz6*^MW#~*opJOOS=cg zc#_zHX1l(mL*^Xdoaw?dFT9f@`!O9pu z;*t6Tv#(rUm~APO%qgJXGgV$T(W`@&4>k=$pPHwr!kgnwH)moi{XQ@Ga{$Y8x4jE4 zMb1xbtntXa2unrnJbD*)&S{FI<`pHawgFrid9@_76Zax8qVSz}d>DJ-6&Y?_X0!@` z6x!DYyRz#&=e?)`oB)!~)==e1IMq)PjOwx|H5Hw-AiltLzefkJ(YOogV9tqMx9G%P z)N0#n_W%y=+cP%)E#1=a8mTL(f9c%^q{M6Xgpl`0z+uK&DwcTRk6p$O_lB?{}@2mF7CiN(U zPHPMx;uJ29O%1u|*Ph03egx-7wvrE9NN3v$u0`RD!^m7_R0d9k#fPYr0!rYZs40PcL1$I%7t7WJErp^PDA z4mC4<*HP0Iw&nL7t8YVd0e2%bxrEUv?(&Iu|K0#VN)zu;v^tZJi$*q8 z?hqgqWy)@lV?3!$d(CC9EU+>^Va-$X%{y+TRCJbCd9@?+GHY$y59H{%v85!Qoy^nJ z>`A9b_O1Yd{m|MSV@ZS)8%)(0^DFpO!6$Stzl|lwI3JV3a@6>|8s$rl-F=h0~YjyrAh~rlbn-0J`VMplgX- zB|vuejREGzw+t9v#b&hZcUrNg(FOb0_*m>%bnU_phQ3g2U zz*U)@xM1?kWdKz78C({O#>Z1C6PhNzlXeU4KZH-efM5Pw7K1b#_)-r2@_oA(@Xc>b zy*C(Gh6g7Qbj+J5D-V!YVdhnI>j0zd-Kho52HWt?rqx+$jAfYK()gdqu$ zf7NF)i{f;OQU*2|uw;&ZW5;u zSq#$onXT&F9ERbX#avwaGU9`t^|!O{D`?6%7*HxoKFn+@1EV_kPsGPaMG#ngiVV6Q zkot@J0k8LuVbFy^7a3XYC24QN)}E;cvpH^V9M<&vQW+Skww2t|(#O55-@0J5Du~W7 zq%;>8JKKjW5tCFJzmz-dY$RXyF&?a-ft0&<2}s zTS^#G-oCQxvo&u5Fa7b#JI19jtB#-=|Kqq#}T<3yg~WWCI8~NVJ2r_K|2G!b>zAE>_pxlsIQL+TZ_cQ{BDy zv4t6cE#k%*917Q@kz1FLzy9@K;|Ne9lxPYB>b%C;n3=ZoSSDp$XBH8_lzkev8g259VGJo z!u(R=_%ZPC zvA4s>WHp^1Lm4xw#iIOaWuFs2Q%62U(&s-%;uLiXXD_Ss-IYl^gp1c`5<~YCIeLbU zMo7>$!*plIKs%itOKmV(Rz+7=#kcIS*1_nq-@}+C&7RBp!6>(nU@GTu3uzw`wC|(g zD^xBv2aii8}1FR3ed376Y@JCx zKjy0YI!85m9bmLnh1&dkHXvt!6but2u}lr3-mV7=z^DS0rp8wRMvBka5J5UXhp*AW zE1yc}X!8ImE7ms}*n;Wt2eAKO;sRbg|3+?0a)9*D|EuXZq(v&Uvd?wDTUKrhjB1Uk zOy;;M7o+Gi^=9A!X=`^}Hk`9#E8Lfi7BdXzd=9)581yZTOqy7qVF=Q)_@^$_3wm^(21udHtgP?p&W#?A+St#_g zG0dn$B^L}+WEf8v(paT8FI&T^V3Z$&w;{O^Iyy6U)O%*-D0BnWU}UeCNIp;(MkJIhlSnhNgZ zwK*@;kxXxX_8LxKTUwvgnG5yay<8BjFc*v#F{5Jp{QXfN+D7*70fJndApK0GSd|&P z>U^Bf7=DVJy|g~U4$|5}c6_P$q6)a|`Y&QeCFizr%qTAc;?&f2gLqO8DPvGG{jURz z^4kN;s^MZtF0DRN_j{kQVFLjvtI*u)m=>b54f}^yssi!Z!huG8cbl?8VC*vP;AL%B zVj&yMj8zfRknz+qz^xbfiFk#`k67NVSiFXX`T$c*ImD7fHoLT?0OI0@y-F> zYt32A(!2?GVb<;Zxu*W4+8ucCKJxKT;m1$Vfgj7LZ;Wpli3f1}1KK-7oI?rCnFB@c zePDr^5=}pEb=Ago+Sdi3vbI&%vPRp5S_;LSV=`~C%5g7ZJJbN9LSK3NOSpry4k6l} zPW!ZJ;!;Zl{N!uPZ}<2EV@u;PH(<0WGRu&l-sHOy+gYaVHG$DGV_A5dIm~DwDCOM3 zGgX?3*-3VGtb=~v932Ke%}`x&so8EW&yb5VvoVSHhU;`D&H))%m5h5aGs?zX*zV(V zB?zFiXZD8kSN2Nd1s8hG`3$?r#SsjAZoeQxwtO+R{PCU6vbVDPS)od+Ws@p?z78;& z^ZZDfV@OFlQESet7&`})rod?Gd!4@5fcL@os;&f$&v2JtmG-~zT?0=@e`?-+HPmZY(OdI27Z2G zpt2~~G!y45X?D+%i&JwGm%y?^gwZ7aX7tRFlnOx7LUPqNi5ac({VL3;?y>SQRrw6f;weVhGEX@PEV*j;(o|l_5_)>)3;;Y^ z;`|7SN0Su&tq5A-lr2W`Pg zvjtIPX@Qd1v$l&XFeL{rMTUJCcD*WafCMe1vxh|O2?Nc=ES1@o=6cf=w^wbu4lpV` zZ@?)*;u+Flgd|#VuS!&L**Px*Mx{(kydO@0CSB5tuW^@OLvraC85=&ar-?oH58t;} z)R{PHY%-X>xsqc@Ynf25+V(Dhku1@6$?G9HokP@TJr@V;Jb*9%0whF_p2Nv2cN;mPmd_yzjn337gFjuSurGkCOZ?4qduS-^}+OO9u+6c2d2$fNhspZweA zqC6U+=dYFr*1U{*=*bfpUc%)uoIZom5D7bQ_^yvL2^p{EGxeQ_*X8AHc(dnK3P5mk zU{tm!7^_4*J4*e%tpi5m@ht$Og+aA4>B9%fO3*fjDiJUz!^kE`Jc85Lu=PE>`T@?4 zOpO=%nn|fPpmd8e8yH)Zbugp3Z8zApdrGc@8O;|_Y(n4KHl~pzrt0c<&5dzAcTVlmQ{Xh$C9sxt?3}{CRp~Sq$E>= zmH^VVF{47hpp9on*9b;aOeyzLj3H&}yD)^^LvwXLKepA~u)q3`CQCkf`U*KehT#C& z+Vw%SIh9vhp4malz^H06b$|xY*|8Kr>X0}w&$;djD#+pmOk)G2i&t>=!qhLFeWblJ zsf*(QNmM5e580S2>`S+5Gs<6QKSb1f*o_i`evAw~n9nf1>tIHO`;8bQO8U6VKah6+ z0P&eGL$S$#Z7&=$=-WptUBFr8`zsEb5aLoa&63L3GcMGks0Mv;bja14@ld4 z=DF7ES}mlJv7D7`F_|0eun&WtF(n^pX>Qx0S7QjJ2~FpLQQ7CLs9)4Vg3uz@R!faDX0_sq85Ly92g2-6fnDy!SJf=Jg=P{-^?^n*iPyuLDVQUJKID%1% z3`QQ1#t(_#=bD*O?mCHtXxhhh|A_1U(XuwA{ralpTNQ6F3dCH%AAJC=ZO@Qydu(r= zXf#HApC z4I>P_vqL!9He-jO8jSLi6_IPVptEP5a58*}OU%)scjC*!`$;uyTP0eF zfYAKJRC7$Jz?kwk;<}hojW!dI#GDyo4@QI7Y>H#`*=)aSV@9R-RP1`DLtO8BT<=@l z@W^}GZD3BjqNrE(I)%eW@Zf#ZK(x2Kiq}o9MPo|3Zm9qyr98LYm6_2s*o@ZBj0)6b z9c*XSapxsBBym2#)R%fd)x6f?7`kUyV@hLN%m6Nqk>lskzXT1;je2JX>9nnBSxu{| ziK}dzXCcz^*&I~i{3Y~GR|nRjxWw#Amq!Lp@eol#+Iz^>fq{EoedpBJQU*-9ah6Z^ z*UXG4sO!WB#QKy$!N`JQr%G{8jc<)Mqv8XqYKs$Tfg9rbPjUSxR*oV*yBmNszP3K4 z*4}{y%I@7aP;#kv1#jdAWW~VPMQtm=sK&l!-`x~5x=yyUO53qo#J>V^m1f1_h8{eI zAN>gohDba@&yOZW@m66OS<%1l8T2mDQ>*$aqmeNU6}bNagdHH7Ow{w3QQ2fWKDPkb ze+ODS#*AKn3xh8D`a1)rtAdg$^v}@#84S)L9^ytr0zw1Xt$UVeHa{-szzGuAJ7Iuk z<=0<~YbZ`pTOX}hw`Xn`!tZq~K$Sj@pdvaz;(AwK6UwNRET?n+*3 zcs1V0+^H{5ERc3I0Ch-v*M6`;4v@RA@?u!D5=?cJ*)CCh-|&q%zU)LY$zhjQGM0gn z<~m!-o35;F_McbTxe;#o95?vEs>hCwtTe@45_R?)+C79vADZeef5MH|-F3?f|mq8XvOoo%5%EPu>(AlaiD>z8Co4nD|C8n-eseK0m)U>4eH?ZC0w4OmnY`*(7k{J zMYM=@C05xfzwx3RkpKbu}ZE1q??rbUUsVh&7Fud%8hfGes9#a zl^bI&AM3dMc@QD39sBW|9~*F_$$D6Z^M&#TJ*0Pz^vwA{f(Y6>mY_8&XHllQD}qtk z`2`Wu-m<4Kj!h*s>_Y#H<&b;PxunL9MqPUiyXFSo+CieNv1|C)rlkJm6w=IYl)nKL z_imAzuH^4pV3ec;{h_&hr*3MZoUp12HQmZ!_~%W=-?%|4K71b zCRx?k{d;i#J+mck?OF_*>UwONjm(od4C$=`qiakDwCp(N+TRM}3_!(JZsNpr-#^(w zk{EsM6R|%3#EZREj6 z@Z*2iw)fA`%U9;Qy{^QUX^i%d(PV)1PYKSRqJv9>0Dbr)Wcxlyhq2;w&Y@`l zQ3z3hW*}U=cZ)!2Suo0jg!Ak;^8hREzm}laA{dqROO7D{__Fm`8IZdu$;iA+Uq7+p zeftkgb=NpH+^EdAA;F4jtqo2Gw5iN!X5R%r?93$`ftHx(9b%qj4s%ZEo|(7VI>e>s z9zN_M!>*|X2bWe3A_&Yqyyat-DwxugZ6yB!@75k#DTy$${)Yo!a$}9r|EhLP;(>YL zB?C0>B1sI=Lc%r#-bFlPM_ItI%YDu~^0IOvRrk3Hta95ICwTRYZIlr7hUW5}y28Fy zU{rSeDljS>vpA;w_OT5qMn^~J@ENZ613G*T?ke7%w>P!-uG6|qHFE>R}&rn3nDPxHY594$|)BvMwTj~ggF+qQ5b&#C; z<8lqm2&V01CH+fa)qpiYN5{C~GqnE%H+Xh80SP7xi=6`tl-<8KUW_!JcGoHkGBv=} zR`WN-c9w2abDV2aTds1BOR79_T0Mzcu)T|fAqnsGmLYjS zavqQd&vE@Hxc<|-2}mNga7&%VB|ZMwVv@4tyiI9}>y#WI{oiUhHOvK=TLPo1eM^&M z)<}@L%yF&@$gXlN0YX7qX6KO39)#P-`4JpH_d&C3*6(X-U~&2aE{=S$yb#&F4_kXk zAmgN=TbnpV# z|Nia*5_mw0&35$g14}fVC13@+_YEdphb-yTP$iqhjFxG8m51uu#(hOF%9h??00;Z% zr$07twc{Vm^X}Q#7E@HW+H4(36X>18u!qJ2WY9+w%daNy{{#*m!`7a~80GTeO}QiI z|5PqN6UPF-{#MBP&xC4`Ayd67F~x zcK|oeC?No8;2jPWX_=e227XlyO#0VW>mOO(@vk5HS>-- zG~^{7n(g)YxrIIK_=VBbxxWGDxuG(xva3oRl*v^c0+wmJii&Ke-vTqbQMR+pxYq^j zGSTmEwJc{`(6Shje%F}Hl@gfFkz@pe3*_?HN|g66A#8b@P|F-1GFy@Qf#=x4_)bmP zF9IKkd*HpGUYoN+I=VhXvrAxj2K`fejk;E%yM18xC5gOEDV>zEa2Qic9Hf&NA(3E8 zg*TO$Hh=G`RCd{2O;UoMu_GBJV~2)`9rT8nQC=iK2))Yg1RXxd^}oaQf4I90DU+>T z1ESqSIDBk1la%XW=Z(OXQwP;mf0fB~F_={t*ln`3aoaVr%uM|i2-=C@;eF)GpPP#5 z@&rA82Cu&NNhQ}fp{p~MT0Af}>Wd?^du9x3)Q9(fgdDsJLB=v<3XICtcmjm5`w)4z z4VOpA=`%l2BrT1S`YNh<=Z1M{PwzgoqV|Iwd zJ0$eZ?fp8$rIyHaZ~^D9;PTkiO+g5)E$D391g$c*8O>Im)rqH`cLyhIBduLqrCz)+ z@4KtiP+E9zoY|5_m+0UW1}CP9jJAz21zQG8E*BS+r5;Mm8&6V)xE!0w*OtoPvoe6l z{&tFt;)%O=S-qa6z^F{MSEZiIZ7AF$LCpC?6Er^YP7UAT25$gE%FeyBW6lkSkIk_m za}3G>b0ce!8mWVBlVL2g?^SG5>z3`aEf+cnn#{X;lWP{_C7?yEhYu>yt*`1ganBN9i?a-p^2g$ zMO%>t)B>ep2PKSGQd>w!k|J4B)v9Np4j7fO>ZyGT+l$|2YR4(YFwZmN4oW}fWN7t?x~D$lL*J5_c6O1dGh30iVq%mqvN$WO0<0*|J2pGdwTePI zgrtv-&W$O>ea{eD5N#tt$A^SDa|QPc0`Zv@3{0Hmq00x;RuuxwyL%@N8IB1017sA> zj%{ZDX@FS{jH+!>lh~T!IL>|1t8#zmF0aL+YKPgL7{ayN2`d zdCqirioUu)FP@^k*T$CO0l|ZxLg(RxDdo#@WMDJ`9VTcyLATntErqscOF?0gmz{!m z*Ydn~UB-?o-O8^IjAp;n+{*!}m5hH=Sdw#Ak@@6>*^~C}8%sif_>DQuO_TXHB)B~= zVwrt66pXH7+gj_j6o|yp+R~%=2`f#IcwlU)e=)96Bof(}!pLkz zOtYm)#ZsHNC2S+DZJU(ik&lDv!RYdu#-E*rGgVDGFvb*j?YO@Flms19;So;8pRuGY znAX=a8rzq$^C}hs%)IiFl%PK%7z~l2FUUt-Fq)^@TLz5EoE?;LV5}zS=rwNegO5YH z!dwC9i`;);YP)fzUhZt_z73OHVC;WBUZ9$;74ci#SGV07cEw>oWW zsuGtOQ=PWiXD9aB-h=y(ER#a-0{T63(H``;Uhz%>{c|{bh93W5wTZ$lh_(!5q7IpB zc$O6@Z(EYovydpV(ig2An-qITX!rH?xwIEAXxxR~D|B!S!wW0L-r7f6yXFd=*^^XF z#g&aevjvS3f+VvC$*c;`Gpt~g5cCEHNO3Z8keDg=Q3XbI92k}bBmX{?I_M>7|A_(8 z;Q8Il_FW}mWY6T550t(4t}U9bOU?cE%7RP{aDB{Z(Y{N!S)&X3bvbD)lMd**SY~FM zXa%s<@djEyadyuwLwK58uli1%fyS_l3@*_u1H-5f&Zol(eS*`>Q*g|bGPRSH+>or~ zhJ@B%-1CW8M@!zERnKDx=@3o&koJ9SRE!WHK^q~mG)kT+WhE^vNvrq1^B_C+#`CV! z*?G$__JA~+lq=7>j>BRwni}^yz$p78XG0pkzzx1PKpNh`@)K2(#gTP(;L*osPYNS* z1J7TRMy@I)fHaj{b%TOl=Q5)e$9bb_yA5NR>Lyr49J_!0)kXzv9&JVn9| z678F7xU}s4yDZgD-ig68rZ^!OC1xj5u(hEesal$#Zl7QnPrzvE_|u?q<)GKPV3cP_ zZiE{=C+&ZW>;Hg`-XK5%Q)PFy%>Dbp2gaE4_p-@vw+@--_Ps$tuN%pBmT9|zEHiaB zuJ$&(^N~3j}apKRppVoQ~}M zO`4$7)PiI$Pc8k{<%uoiT3eH3tc|Qy3N|E}9~j#(qb07#h9#XXwh`|R9<{6#dS}O2 z(#dNB1=X6JtwGY**v-XjIDY}XV?;uvy^FNkHbxO+M%lO&Uz;efauMxaCa&fK%iw)u zsb18MmGsg6E42R-(t({rv`rB0Tl%I9h`fyb7v|)r$^t;zSZ1`sv9DuHcg1Ej_q!|+Sztf7C^TJpd)olvie zBtxr6d+FKI@Y0+C!Zy<0gQ$gQ=soZ<)sc4zk9Mt%OKiV%U~K6|lVUonq8eYI!y_1- zo6`djwDyr`$LGS!z^K4P{dtMgalNI?7*g8D^}iwQ{T>}Y zN0ak650JtL+B=qX_3*K|fJg1?cLI8QWL@PauF&SDz-ZNRuhez}nNhLrIJu`z^2G&W zMah2jCsq{i>=<4>hxCuem~P6=)urH4Ghj4j(bDnqk zfxFaPt4Cd53gQeIdTPq$sr6zv~d0IMc93rgejh|%QI7*l)> zN#82bhFj(y9<;2f(`g_o?QThCl;6(m1Udt1%YYGHjhc2zd%q{`{uUj+Let*$JCoeB zh}aa_-hxLTna5pgt6)QF(CG?t#qtwxGZ>XJyA5VWRbZ5fFDyrvy?N_C^6@XB8>0P@ zB}W|%#s%qaRMHqd`vL78BNs2>{26k2M39W&!H1TY{e7jpoT52O+x;&|N5^{F&mP|Vin$vt^98& z1H&?HZvu?2%MJ$9DQd>~)<9yAksw1sh5@2hUV+phacrz)*t-R$s)I9hbm3R;H#~c&*_BB8kn|uvhxo#5MG_fP3b%~y zP1%!H0iy-m$t6f#$f}CpNui$rk_*!AZ%MlUh#MXGv_WrR@==$X-RRNB@c0w6Ck25| zw%P#F6(z@z63A593+2X~9j<6Ix-k&FB`{jrfgzJz${EXt2{AAdWCStq{n|CIQj!Y?-c!DfIRpTb{+$vPsd|5gJkbRf{(XB zBD8m8fYg17#=S)sYW?C8G`_@Le1rC1;4Z&MM`wgdpYY%-*nS@gTIe`K#hSrr%#2nA zqx^erHKU2yj@;WU5 zOxFM*iEk)pT*=9~bX+u#xMvk=M_tRjo%*yrVaKwEOK7&HaNAt0U5co)JanVWTbewb zP2g!CO$QcCn)Y()uD}>w-c3BqRN*f{{&HmBmw#R-Gok=?GEpwZXQbWVkaYhMO-|oj zwH;U-gy zjONLeRBuZhcN2qN%e?8bcNI%mns^}Ui@gRyFc9y=!1Sb}Vusu8T?F!HTWtXZqP6iP8Dx(r)DYYi=#T5C#tcA3OT(=Sb+K;n=4h%45 z56@I`;(>@%j2R`s$X=58ZN>RRY+v)b6WHEI9)AX34D5$DI7idi^3Wx>RAxXOBtz7{ zf^>u%^(-E^)(GxPebQ6l|HgG2HQKhtxno{XV(3tASA+o0R7I z5_7Lthq?i7^pfQA?@7DA9R~ovu@VH(YFm)(;XCl~Bm3JWlj4butRi#VzMbYKDGy8; zJ93g^O)_aSRa~s%#^h7xFOnN$JIl1aMP?)w-@Dwlj1@W0d}1+&g*eBK9Mc+=MK-|f zJ!-+hBjo)bL-)*>QanUsU*AY?F{hVHVbn$AJ`9d=y_W`*aUTZf1oyv!-S?5!9@0J_ zc<_~-SpPK)PH|`77&zT%Hi!JRc4#_4-3a0fbaY}FCe#roUBcbZq5WVIb43PB{-&>u z8L^;kzJKp@;Ra}WX+ZQQ<*%y}_3qynN5+uG;Sjeyus26S{*%A)fw2-HL{s)68UL5N z=rW%CY;u$7SGFzhBAQ=?mrjDkW8~y*W=8qTQ*=_u04VV`ppg&^17k#)v%=79Ly~zq zXA_l_+y}^S$hM1m71DbZs^%k@0BH4O=GjmM7st|(Uz_9 z3C#_AaCCdByD3S{V=cZwy=_U;q5UMI9VFZ}mS-o+RI=8><(~qhDwW*KvpLa5GL zRI;Yr#JK>B%3Q|B+7bdxRoX@4Gt~9p4x_%RWy0@YSheM058{yl)HpZcZ4dHpOEl#@ zHv>lb?`AiRHRdOZAm$GZP?0X389wMLpg}J<<#!8OAGf>);#G2~R{t@oz@6q8Idhr9n z{jbnRzaTjH9NBtA^ytr!G(`}S7ylA>^7j_2bnBvMIl@N$ocwV&h6Lamfjq_IWF2V#Qc^JTNdC_G~d0gl1ow zy3UQPBGF`mEV7;))u{s>c;vl&4PN5UD5{bv)q zn|vO-Xy?^&c}C<4s<=ErR9&8Bz^Kk;&Ok2^D_61q5c%}ay&5;PKvm~_$%hSz+Sk-_ z$+-k^F~ohe_Y%4<2=0GHu=Os%{%3^m{=1OIq(}c6cmB0GI&6ZdL(?v<|J3YUX&*Ou zLAd`@!o43Oop%VreOslw#8&WR6=^GgD0@EVfzjOOKz-8KGc-O%U3Vh@{Kmvt*a5?? z0n^TbH^aXLYRhYuXf-gJhJYJ52V3z`~g?%->UgIaNwQ#G;Y znM>yIkq4flO(?H^D*_ND?=t7OED1(+&oRTAsDr%wCA9a<>+bRhaS3|<+=}FFiewY$ z;D~g1g1h_yckzVq;Wvbj{~f}^pAhbUhP)eth_~IGZ_Xs^5>zj6ZitRv6ZF1AhcD6O zf^hF=$j&E7xHpaw(##8Po<^Y#7!?`T__v#yhh1_uW=L;j{<5Tn3&!$t9(uuI>G?BA6&45o{_mmq;9?0@i`E1M?s8;Ma_eY(3&-O3a zfXoh*`<%B4W$L-9zo!9f#;9ET`c~YdI^UITFLQK|5IP}p?|t~>F9=3`w6z6){FmtI z4}QhI2@{oP3w|s*JjR{=9+Ck%>Y?3d1l#XIxJ|JCBfB{rn| z#fb-&`Fn8bwE+ds;zm}IXqu(?KV-oSzX}*--RPqJs%~l-%1oRkCd1d+-O31ErAn=0 zYk|EZ|I-kW7P&|68gi{zWXPY<#OSqj{iIGN!5i!#48%SJ2smpoR8MkhqWb zPHr!o!#we9$t68p_bKk^UlTs~CERA2Rn^Es%9Hqncne)z)E5Y5M?q)Phg{oZ#{`8bt8)-%38=iW#UW#lz2E^i)I`@(I0!HVk8*T{O!|Y;dinwSyHr?}pCgu6c>y7!ladp{-I`Ur^*fWWh*JO~w- zQEuBzpyybNE~q-x_0jagz-Y`KH)z3e?UDh~x!IADEKzUX{`NLqkM&73NfngE7i3?p z0gR@kCTPstzG%W<_`bmYm1iz>`&{O_EF*PoS5?PdrES$UnK%ACX$V5teTcl@F;(69 zOVp*v%ip0FuWm0r&mz&(ml(f5;tM3c^l?7ngiUPZl2=Xr=wdYOS}<*5r8P9^p}QA^ zTOS%IN!!jRON@#u+~+bdqA(u>zV8b(IRn?9s0H69Q4Ej<7iL3>hvwMOV9=E%B2F!+ zD++vYm*ys!H8Z2S6LyUWcIoM%OkY>he=SvA{#{D`E*`7QC!#(%4_CW-@kKv=XEom-Zpo2@~RBOgP1KoPC%Lrj z4#^oBpQAb=aw$RkUGQFblbl}46fl*5QMOqC+z=NZp`%x5+THByah;MR7TPe(0aDMXBp|zk#9-bwl%p z8=a!@F{FJ&0z^6l!M3kVo6qo&e`lI8Nr)i0_%%uQ-;i|w5jT3_W03C9by{*N_{fb+v|c+nXI6?TRU4= zG~PP4mqoUwHI08Q>3~YM$-K6U-pA>M#Il1Qyn}x7Z&_`k!6n*1Lr$NdquypSrMVaB z4u~dJUOesNM#s3}Gjw!>rd`6_0c?GOga^4(%>Q2V6Y6yQktP?o(M#OurDbTitJmjB zq857;k1RRwsBbJOY+(fzn|P#EC89sFix-`lVeZi(9PV1RP?=JiiSKequ!%bedLfGzub?0wfUP;y@p49-vq&_Din=<~ma(Om$f zJ1#D^EDxHJxELKB;|5P~yI&D*eMZoJABheSX#Uy~31UGtRDTC6% zZJ*ukN*WswWq{P)@!BOX4Ja)w5vhY=496D`r^bxJ5cal=F^!WqQghY4f-2^&fl*zy zGw)pIwcB(ssv3W>FNb;FwYHJN_u$jNvJw_)jP#Dtvmfk7y(xL`-bhgcleA0P@8X8f zNr%sfhOf}QW1`)k6Ks79;l6>=`1hLyRG3{v-N1ri<0CXVLDLP&?0#F~QlDsbF!8i& zGB#XEEHE~8Fo=PkfsiW12ga1z!Z=pR>4Hi54lpBX>{GUV1u&XB?izNSviGIqjOJbM zWx!|=kj`U*4kX-$!}sk+J2*$u7{32k=`e-$TNE@ALq^2$sKxF&dwkLqpmqUlcn408h0^L_t*bjvsHisG2iFJT%hUon z_2*qMm2QwkV^?q02~!&R8u1xZYDG+CKq#A(7pdv$*fMSij25~uFY156@oL2|mGybc zU?g?gE^~cglB-VwXze2RKY-8viXhrXg9z6@HG9(A7|(Jm;(@3?L`O%Yy+0Cee?zo? zM6~xOgq@F()Q3S0pa#%MB6_l-1!M%Cq#lBH0k1o&qznly{mU4z=R>Calxda z)9;Pcl4wTHsn|vA56!lOm|;3~$o>v&wa|$GVbi}1bfb+J>( z`4{Hs(5x(PUNl9Mb5a-M;$za@*Myyq2-@$W$qDJ;TdRSjjYETpdO%9-IaN>cA|6M+-tU>n_PA)&d1*S_TITy?Kv4|-FWQPp)_#8%f~Gg=3T zW=tuHpmT^k_>4dU6mWx6NJeP)l?5F(z;uJ+%&z1{W=|SD$HgxRT8}|TxX~*s#@DD- zCz1!GDKN_Y-AHYTDlgcWDJhzyNVkW+6l9bj$$jK-7wxnl5=#O+kC|?&&1lg|e!=lp zw(YFLW>huS8s{~`jFthkbzYCGM0@1bcbb^`uC)u_{r`+9(Znd-ny5pQGt#UU)Z3cf zd!9J8j3X|qH`N5jUPYq0-{vwk=rBf-3p90zgop;no}$|=pDDZoly1w8QkP*Yn&7X( zo~{dwRyoIc+gDJpu4UXA)`TtCx`#ac5rGp_BHZW_h8Jk}2wbM0Z{%i0Q8&ohjG7sC zMy6DDIx%-B{=deCw2Va6Dv^SFw>mUYq}NA}Pa#Q>c)!@9}*t~HsIi@R*d)fXohm(2CsP5$Js?DjUmDF59GV02yMzg5@ns=jVw++cu|0znJ59w3hb0u?me#<>{!r)E=9*D$3< zHcM0?O}*MKE90Rww$IfhS_t4$_~6vihV=RnoPjEmq_DS#xE6$gDFQRLl$Bj6xQJg% zIw0ok!-9Qk+HICD#ZaS7ZJSYD(TcyT`qu^f-UOq|zcQwT62v{60?+>zJ^j`k9U5t5 zQ)JALQWjQ6Xk?*83tNmYYTj68(cGyJv?4SL&`5D1NF>IZa=Tw!I-o_9 z`Mm85+TLo=Yh5Yn>&Aj!m%T1!$Mlu8kqFtk5064SPUzDN4bRbdu)>9Dq>*b9Rbxgj zr>=wM5nWLh15%YZuf9{Yz*z*@bFjOMXd4^^p`fWEAqGl;RNMGg1*5u?-&NVwHGolF zR<@AUX~o}l&i^*liwnjko>EI|5AJ_Tpx}}o68GW7-=P;zVbrbBaU+dfx2S=UbLP}Q z9+b369kirGE9p9OaY`9e(ljM-4nl8N3PZ#LkOqpj1Z^oA%EaLTR#q3)VlMOgsx!$i z>i?!D^+o4Y^7qC%UgY;xs;_qp5JKlZ@+c%w2SFQcZR5WA7ia@WjckG_o*B6UL(=K@ zMrz4o8&a7__MZo((FjpRgU~XBr;2oTkf?3V0vQ{wC7OWJ*uGSk%`Bs0n%CDd?CG|c zQ5`kc4Kt%Fk2}BbBCu6#;Ugim50Lwx5jb#ZjEuSvcF@Zg5I4Zo$ZbjnMj0R}O;GIW zvKkINdriY+_L0>DDU>&0MgR3 znhj~{Lg$#$aA@8p2rg_9DA3tP!j^%Oin;fuSgDH?%k&WD*wVt|QL5@$IQdoeQ`N^N zG9xPbxeLswuG+M?ZJcJP1zY!#2VW3qh*L#|=e8Ki(hM~-KqEIUg$H^HjFcuYb~#zi zhLnlq{t(ZUl8h-SZWI$7K7j2#2qJ6mKvIPSWaQaW2*IU-amAjlRB-TM#L{!x)&d zzZ!j`clhh+{&AB$Rf$hCM869X;F1K{yN_(|S;lb5Zk!?s<^&N78mkq=$e26Lh$ zHtS+A*EOkEP4Jt}j22w?mCL1Pi5j9^uBR3_ItE63! zhOsX#S7S(Jk^>u!M{s#&!LtOY1Y?`hq}_xqN$kDvBG8t-T+2qO!KA3|Tv-;3>e`jA z8;lm6OWn4&1x8C;!<9i43TcM!BM$-siO|+AxCA%qBA3t6bYv0qjWlxY5}4x+lew<} zqt#?RfK+O`6UG@G20aK)aLyrn_dHW-k%d5x`|vAZahP}2NC_h`aPsMhG-FHoMM)jw z_#7~jWkIP%o7y?`SmL|f`@#|ux2dL^GxoJGy4m2v8U{o@umDO}*=Q2(+)IvIk z$o&A75LyQ~X(4eJbx$B2HcY9J>yr$SfWGAu#>{p zo~iFLJ*?Bo$~U`_saNSGKnUV(OxgR`0U~2eWf{h^zvrof=CxUueQDA5dHu{A?`?rm znbf_`#kO^hU$UIbFPzsEnX6U0HQr)|3kuefpizg4(X_eONOXxyzd&h z1`+WgEfEtN5;UoUR+F1yNSX!DI@DQ0Rwp4yfH;SAc9F21qd7V~5oL@in}`yzNhr=S zr7G4`lzG`8>im5bm0te)WmJWWRFcg3ejVdp1*q4l3N1=@53~2gA^wLiNrVKCVCPGM zdlyLeoRki(`z>yC(tuJU>lN|px*+s)J@@c8Hl(FxeKw?p5=@vom!<@1>X}k7 zS!H;K2w;{aWWN3mB=+|_@a)JlCT%leP5BHEQls<*C7;*Lj9BnpzTaikeb+JWWx%zp z--f%dM38D8LZg>nXu6bgOG%`mdXG5B@Gtk(OR*)O8Ar(X?H%-$7 z2A4Q>1W63LAY1!hC+k-mUVS%aMpcq`7ayvz=WwFV(+FtWy9R|47*+92>tq;Ie9pEl z17?ekbJI4XRajfq@$*t*4-}_JoFJo^U=Smt5sYFaP9RB z!R!AP+&|#rvj&tJxw?p0g@eGa?Prp!HqX~Ga)T5^h_T@T7~n1iIg%qhmB3&bjc7 zG_pXX;AQ9Mpr!=HYI5@osVELq6BzVxyoNMJb{>wIl1MOr(k@+%WUR{fpIV9{8LFx8 zLeMPBc&L*y>!Fmpk28HtSr;f3K{f*oD*9cd{<z=wi|UQi7yzrOR)1Ptw;Y0q;*Jg^xu)5{T+;sya!%0b2PH77z-zC zS>_Oe0vI*V)UtB3Hl!Juc+_b!gkEBf4iZ`LY->Arqp6(0CmZ~GL6s|sr_rMJRR9v| z7?`x>ZBJ8<5M{ZkDo$fnFv|bEh#4)K)Ncxms`{VTCX>oyj5@8Jb!wh-83+wxg3$;W z#s)%3210&+0VJRGBbyLX(A1tKB7)XE?fdE!|NFBk>eWbH*H(Lsh4pWo$^onG9m7JUG{2V3< zq-wwvD>5{Olrg1@MG<6`Z71(jc@*d6)>{IUWYPY&4Mr6#skV2a6#g#UJ4qd+-a#St z%qLEb3B`#Od>peOH83h1TV@85|K6dbo+<4RY=1y>|KCD_Eu?ji^!)E|gBNJhrSZUP zWGRsl!nVC;7}9MI?(WI$F{G)RHAzaRNeumST$%y_LWH#NM#EsXk1~3!xj)MC18KGr z&-SyRTVqamRi2FBtvrT(Cd9jXvN)z`;dxh7OucO|V(~r;$6b2P{tut_N1O+u416+` z-1wD3>O27Db*5QrDxDt7%rhY4`->*12ttU2yM%jRA<;bu+Qy_1T>mMWT-LaWjWmKi zgFy&k$DSj}0aEh_tt7Xsvdc@xfD}zeHit+=s!Gr~FqiPCZ4W?JdA4LCnwgwtCe&#= zQ{Mh0Ste zOSX+rCUH(86CfJ{RBn|Il;$^Dw5rOpka_Pc_^#}@HGoW}Vl8~2st;Br&49?Wp(GvK zhw^qJN!$deoGS;SIk!s6?q?+!<(>imx8lr$Gu$EQv|#TmQ{4qyAT3<)J2XBu54=Vi znG*k7&cLX(?QKXhVf33guigQ7$ka zqe7c0Mlp}sr0NR_x+%X5$p9x{${E5Kl#Cga=G1&DSuP`)fY!2?sQ@&R|2%J>vhyqg zS<#l|vm}WBn_PSl8OV5dY{q}PiLEG8+bI(e%kE7e<*Ln+`zKQXGr#RDE6ymdLse~| z0R&s`A>od(rJzF+bVx6Miw<8dcTXC5J0yrEU=(dl5=4&|R+~7lZoX`g4XH%@gr({r z8AAUQ7Y_(7Ug6?Ca{n{feT=mCtR@M$Np2M|Dz+_6E0nQ=iE3|Zr(7IU#-cJn(m;&W zdr%T7f>X{Wm4Vb)%H7ayc!f;qQ|Fj}*W9FDaxHSKX97S>gOAJEgffpeua=W!0!*YC z80lEAE{j%VE#ZzJGU_bmgX z_KtUI@MZ+f*Uo8VS=msA1XHt8XCDnhbZ~|o{}Fxpj|8uOi#+}*;ivx|JpK~e+s01P zY?VR<7)^gC^Cqt{8$}rn8Do)-UCT|}co)dIjOX3PMJC?wQdIQ``AU>iP7^uMk|J1D z+J-Wq$uksYoe##K<2(b(RB>(^h_nQpG6t2Y@ba$sQlMv3*LtxXNM`O)**=T!rxf?s z)Hf5Pbx5@T3xenXiT07`kmTZT(f;?%s<#m@Vt~}%gVruY9iKEaEq^u}sVy7Kko;sO zGP&XHT{H-w`xV>B<+lx{kppyPq@6 zrsVI1U{tox;`=$%&$x|`Kaw^<=P@GQCLj@LN}`I6j!-wCDPhqFVFx<<=4cgqmv5JM z{cU6w*( zKPYqE!)t(1SpaU4eUv?rrJ2O@pg89qc*GHk?K10!7QiS!W}QCnl=qL$I48{5c2sk0 zifI6vIp17ukDT^$GOUVg$-L`cv2ZBuZ}I&sRreLoK@-0y=n!mwMkF01*d}N_Ai4Nk z((c!2+NF`VSh7X()`794OkD@nb3h}tWP=$Jlbf5S_7HRq;lUR$>Y}fI4;RmIKm3o# z`BSuaN-*jXyf=XRpCO%lle9y5XMv2h_`j##$H!zL7?pmPe-MipQJEW0rzgxZAes6+ z8JFbtpGlijbq@3ToBFOq-C6uB7_A#4vrM0Bl^w5^hg=bi@@=mOMpNGvuUBf0G!ha- zhsbt^Ksp5Bt~GHnZulJ1o_7szCZD%bGC=C=+2S{^DS#&aXhjM6%m3)FG0TO|26hhF0urA$LQs2=$}CE2<^Rw!MV}rXd7v5AyI?`fh7Q66El*zlYHTGW&gS$ zmZsQm`M73XpsU6*e~e{GE#}^kIqGs*2u7F+*< z_kU^3XK)U$ev7{T9q#fua{LW?@-4yT5kZ>3gU?|55DB-K_9!Tv_^Qk(Khc#jraG(m zqDX#>Wq#Vz`>IIJ&!R}j@_iJtNEEwAb3v#TY^Uma<+g1!7?rgx9gAm5WFC0$A;At3 z-A95ga4{(Y$*K3ib2Rdnh={p*cMi;{K?0^Sirj@cbwefOFE*GV73XC#BY$Ws5>*4F z;U%Oaa}7@h=%@>WGqisUgHt56;6@3zj5P(Jxm;&`7gx)3CQzvYopnvRRP-~at<;&I zEBnv-sMEG40L*K<)OBHg+cGs?={XhIYTfH?58i|+wUKZOk&ZDYX`vcpC5nB5zDC}B z2_j=gkH0de)Y|dRFW%+50iYY-e-&pYB^oL49TiX%j?;1ntM3DGe-}gBv#a@7@-P+Hn6fc>Gh?dncDR z$T4>j*hrP+RCaesGSyw+t2A~F;K7d!aE4tNp2P40k|7!oVRVU0h6YmIBlPkG!GoV5 z2OlEa523Y(1Z|slT0{@|;5SU!Bg)$5_Q}1Mmz&UveGA{``?&%b&F{ZX zzl+9F@p;~LSu~c)Yg6}H))`CLXS>0wfib0UkD%Qm+D$-HB-|zG{sV6C1WnG(HN25G zQ2Z}9%h1r;nWPMI4H&IMLjL@Je|H!X6zm;=xm`wWQ{DAXO?BrI`(722KJ;Fr7thd( zXV7~MX@UeTBOCujf-jJQyS^UGM730XiQD&STiR=Tipdt5G%qNLQ7+%aDM{DpkC?t>x8u&TL7e3u8qUBIyVno}m{{(bGS| z#Zz>62IVCW7vM^Rdz{^ z2{)j$g1iX~2|r0>4?+u~9XR+1V((ekKlZkybapb+6dhf_=n}nnik$o&J^hy8^0~Q+ zk1mmWKSFjMBCWki8lp@^C=-=U85ot?hw^_frE;s}@IXeU$(Aa`!b=cUH?y96CTewEs=55#7HXF8t6Lo$g%gUPw zNSU!EuvZZJM6AwYl;XZu-7Q#|sv4V0hLH43r8hW%-YfIU>%Sh`rX&aF${XU;P$tg#1gtG+@50{u@c3swHGFK$C#{onOufo4 zMbiZ0-oy!_e~i0$LU8VSBB9R{AHKzgW&hK{W+8 zz$GWR)0!Z{N4~?6eE@dQ2n1}>gJ|H$lAY3#LzEi+<^w>c@Z22Y*s{wmBU zn-t44Fw|sTxouK+QlHcB)zSg!%=fcdz^kuk)wreDZ~l*^z^LrrjBC&%5@8;8BV2My zI{cQT`}d^1e?!{;9WH(eu8%Da8@Y4h60dFXAtzDCz^T1E`M#05l2h5e(SU^Mc_W{V zH_LOE4$VBw&i@*p<*+`KoRQ6H9aU(AdO`{V&7 z1GMpeH2}7@2U|s3ipMFQn+K6HHaG^VVn1c)TIgqdrUG~yqB>M#G&v_7JtG}{PdfNL z>EI8z(Mw!dBkOh{mFx(TL}N zJ8YE5KV_L^wZ1bQ-a=0wEN7|dIz5(dmkZtA0RvLAf1Ou zd(Vo!Ni^{aB=Fdnfs>mYBll*1ZwMHbjIX@7Ue&Qn8drf)>A=g+qZrRsdGEC1bA|fP z;){GOt+6WBfevsQqsavtAK^yNNr&H)48J2Ce1pq^Vw?D*H&43vAp=7;pq2}u2GNgVpH8EeYE?G^!V?Q&V6M2 zF~R=F$ie3X`yUhRegIn!k=DL!-5q9F_*- z1?)VwZ@+({Xp!}0Gd)MV=rwjLNT0aGC!jm(udm?3$|itR}VVPG)> zho9ND(WR;Gy00d-o;4g#lO7~JvnL@))PeQ^y8AwI_&s{?8M^-|!Or{0_9LWyh_?2T zXxpbK@~R-oJhoukwqdX8s;JBXqkJ2CTWdO?xvZqni7o*~^MYRMw4FHtrom{ct%orY zP?<_mlx14bbcm)GxcGR?h|<9yNe5r!Mo)2R&(t*y2)!MW^o=D&TbAc;=dsz8r11M~ zWUjmg3@H<5>48LS1#|d`m8x(l9Q_u~pUjr5Sc@o{j$n9-OJihsj-Gu(+CC)MGUoLE zZ|^*NBU!Wi{(Gvb1H*7;IQhoCTJ5f-6>M1s3_~zr*lz^ua|p5_SeD=be+2`QEdh=& z{5|9sh8*BcmbKFEUEQ$v&b^ZZbL>>%_{Djis_JHwLvlDnvikghnr=3mJ=N9K&%aJ~ zb9aaq-^DGwgPXgJ>n!{9@s4j%6dJqM(#JzTv{Mv>)*pLgMXzw;*L}5>Hu1VgyysVT zV3;bPdLiI~3zZn>oEM^0$Cc+h6r)x0;d6@7Q}W?6igbk{+d@*G)?Mtsh8x(pl;l1x zy|)6JUunkOwU1!#rpB@Jky%N{p!pV3KzxAFMVhABU50Dl^ox9^#k0M1!cBHo|FNHUL$HHF-jKIyQ3-ii|OZmL=< z{9`0Wys;4y@QT-c-F-PE`QRk&fa*odrKp9+3f&|{BuDa8M@VE_6zM9(;33)IYx2QE z^5Ii0bd|KaNU*En8AN=t)D~<#&|Y_~1?XIZsKe=#r)G;85~SLlB3EIWzo9>8Bi-V! z)O~lHzIUhDfr%@6Np)V6WIbfKiQ9cl(Op(C>fFF}mx(&dxbCtFS9{rK>diyh;MKeL zyagpij%HDYP65G+8W$-ywoe>OG~P-FhNJ#=?C*~fBN_j^$KGRbhep2ZU{p2D^cN>m z477MP-J(c0HBlj5r%2Z*(lsPq)1R|VB->Uuo?(C%-tRusi{6Dhu=tK<;7M@5*Ss2* zcz1y2rhOlVh-St(2k{&%+*NbC$N>lGz`O3=!hsRd3_U-WiM_|#B|7fl+E-MV=I#(J z+#_0emuTTVqPe@!y$S8B+CR&+YaIdn$$lU!Zv4!z#K>Q@9T27ZKGu#~?e<*7Xwn)x zkig)o|0GyFlc~4mYvk2&D^jHBsU zt|v1CB;7^QUEFXR*Iz^SpHXzaBI@2yk?P*`>EqWlOD|dQV=?9-nb)>OuI+p5)e3NZ z?8Jq%auIE7RPIMH;R>6GPn9Av{`2%$&gAW+z{ugRbHv|_J-6n5bLDM4AtbmosZ#2t z(2TcYK#}*6yyx9<``#+rEG;3mhSn+44Yh=d9#V|3mBcs{5&bR?cQh`&`IW9QSKifj zW@8sIW+F{8LrD#jcvqcsYH3_~PuH0u(~P|J>@5h=5fmws?jYGduD=RVOzvV8o6a>{ z_XbfXh|@BzvrN>zh3nk%NfAr9WFCpye&TIZF50@DMnFN8LpEVSgrBE&gP<0#3LJdy zQAr8zP~kfIBRlXM35D^_?^D&jXWUW>?zo{Bplx-#jn;I`c))KCLRpUyns}!AU8fWTU_tw)Au5|^MEa2if&DwLVvW1Zg z?QWcAl-1)YuC|t!7L)+ar<*(Rp{bBi@v7VM9n@s6_PhEmrPiGKd|j+&w~JuwK4hz0n3Kngr4+f=It{$PI`RS&w{h{jid6f0 zX+62N7pY`XC*Q^kxMUvUd7mWF^$lUeC=K5jw$y@<7$N2OcIRr3g=6W^m`vTAsk!tj z=UR;eLNoD2LpY}kCG!<-_L*ACm*b;^%J%HxMgnzxx`Pr^&*ulc)?PE3c}^$Id%C(UaXwHR_XF1 z^y70{D@8q)6ZWZoTh9h8kkaKAw!BR8kUWUaz!#?W)iMlXlke3mq5UfHph!#c(xTQ> zt)XJ%ts%>fGu{G{>$i*|KfgzNnsvAL!mlZ9-NJff047LBPz$oh zZQlZ8&kk+p74Z|>|AZR!6okuE0S3ZEwGL>7~)({N*6Zczp3 z3l*l`iUu$3pc;lTjUtE9zOEI$RgEJh3up}1)(uPhJ`qK2bVVEH?`ft?+*QkI{bMK| z``&kUTn)pRLN@SL(hCg%bZ=0`re{1JZ84;iQg1U9cl2Zm!?%h|Tg>-fYDP_wG0P{H zVHi`$y&!d;!_r4EpZc1l^WL%Z$1J4Nl4sy@AcF37O$CoTT4S*OGuZwL(p?`{vSVx* zh9?{NLfLD6?pjY9ua>mlv~;UJIWYBR;N_EWpA$RfmW5PhjikFj&_N8>`4R z3As4BI)L!yQ4hz);+NhR+k3>#02Tg@Ya%ZDnOmRkC>vS{%?>6=Zv+{;(NW=vZ#RS=CEkM867*l{)lreo9#1f9JrZXw8yuHsN!Psz8=t`L*S@nuYSSYPW7a5AjV1L~ zHRjy%f&{M^OxZXr%kIq+CZhTx+R)9x#VSxPQWxI-GoKp1Z^C34vqX`pm!!Xo4D2-9j?n7NLk1SG64KEc=x3c^&o_cQre&|3a^x@}d4svrkOJ zm_bCp-`T#VfoB6~r9QF2&C+*X62#h2pC5wnqzL#?7VT?rCDBZq?k(7T2zy^^1IBF6 z+71~;(+C%+S>KD&KD0-g0Oca8#HdE&d)ZZ<%ErIr3yFyD0v^U$bf7z@)>8Yr7SqPQ zZmIVYQr`+3yT3U4KzU$tG zy+@kx(0{H0(B@w?8OH1&g<46Y9W9n^T|v8lyQW`z)?!GO_I-g6ZLmo0d*LO}xdE+f z(7gkln`$vdiHcFS=jSomNDITb{CGhcZmXr#DIj*U;=eMN-G!tK>G^=Bi`8Y4U-aqfXHVos`h<=CD9ZhVI?BL4WTsQrgg=C_1A(2{dMeo8( zS{m$(y5L$GUus{6-ZO74?dVBulW7g(av?tbJJcf;S#@FBY)UU(f|O#^_;-DI@z)IE zo3sMN^RV!N)-TP!r&iLQPZ1xi_;Kmh<-ss64?&RjG=>ziX$4JeX!a6aT1YuFl2>@C zkzmL=AoA8yTMKF9d0$Yw;2X7Gg`u~WvK|y6P24aVgo`xB8g7ZIiOs>yJuT)E;~bUEQI7cwaP`M$<( zvVJ89lKNOq_I9kNBGdmjA69}Cwe@@6Y%wZO3#oM3HTK;$4w?8N_?aC*tb(-I)~vkw zcVX}n_8&v2=#mnXGbsMMw@z@ZmSzjQr_%RDV0zbsk-c(F9T_A9I5WIBlN9l z6c&R#=NzxF#o;^QvZeC4mVVs zWO&{?Z_FYtQc+13$AzbT)px%S*3d%bqANw{kT_95rbQpz!JnOXFT`=>4ot47hhgp> zWVNZ%>?@m5A`(qN4EAp%wLs2G1H*T3Ots|faS$39b*Azs69P+xw)YfE& z_6j_QLhrE(rAySMR}8C~p|iNI zmoQyAJ2)4qnFK+~6Tml5GYhFPl*P3E$||@rLib>os7S?&x&RDbYMS_H!@DIyDX&T{+TjW&>qHqP+xupxi z{QG*9H(1dtzuse?D!!(HUN~bvxiMSR7SgEgEa%e z6GFXBC{i68O!q2x+*u+M_v{Y#;*8k53P{wz?pat1$NtEPO*Prol@XywLeegO{}q#Lc`+GtQW(<*kX^ zF#n;qi0iPdbJwQYnD;#y7%k*E#34j zP5Qop>+t+rtr!=F5TA^@5HILr-Mt0fcQl!%d$(G^T{K<`Bx5E37t#V4Yv1K6PebAx z9lOL^dZ^e2FT<&7{z58xuoP>Drr{RS02ooFsu(jx2%r|I=awo(JYKA>Zbus$R~oMQ z)K?$K&3Ygs8g|8IoETRH{5!W`?md`$S2O3@H?^iH#E6P|>0_-}b&QLAK81Y?LYf1y zUbIDRy`XC^!}cez_k}O3IB5o6h-DSyR#7u8q>w6ET1Z1okY<$HNnY>v;M!XA!4-Ao zh1gPAEO4%&&j z^164_YDzaNi)pm3;*@S#q3tYCrf^@;m{8}o3eem=jit1%YI?V8*A{LWw-lOmksq?+ za1hPw@mTt$s-|i0a~Pel?NBI$tqW2k4I~OwgN8wn2ACijCBpbP^w3>-PGbYDYkDP| z?L%j?s$L(icyZd$1c+idZZTOx#3iQI-lr^Ry{7KC?mbP0>E2TpZ?fRixAB4bjOG`2 z`m8vdw-^_o`>w{8iVY5mB`a(h9|0~_{0yVHs$>kOzo^y# zQI#E5&K#PN&)iYtxZLnrK?CiUJlcTa3r&6qF1>W8x4p*`;Kqgd)?RLL{yn!=`m8a8;VR=O3|DiE1aI|=~mU8^~mE>Yq9joZWNcE4Cn*< z39twBFhMeoO|2zSFqrBs|6NN*2ecOTDmvR%F-mvTbrEi}-Ku-Cq)om0yF}Ew#U~5C ztM=8Z%}-ed;JX#XK64H)MrCZsKF3m=UF6+&_teEb+SFeUSM=w@sCPgQR#Gz&DnZ&y zfL% z)a{$lx$EOXcU6dzE555jLMcLH?kD?<#t0dOQ48WL8dF-__sv@SS~79yF^i`3@oVhy z%}@;*6C|71W}Je_FG^V8Lcy*7GDW^vGG2sa30gOFu}e3-#kA`u+74=kw_&98$PyyX z6j!Aw$8*)R;dr5%G~8a+?77ymuSr`_tJlT)5@CkWfyp%%f-VWE?IJ*|=l%SLn(Q;$ z^rCb~OD-1yN$q%#y)X?s&M6u?+DQ=E@jsX#8D~Ike1*%$A13k(-@ImLNm^M>*}l5( zvK_UUM(aK?V#~Yr`dWZ%^-ZsdFJP@FD70_-ZP}-5FM0v0TSKl^&Zl�yE}i-_CHS~y&K1bX9EQaK`~1;pwSms*i* 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 URL? { + Bundle.module.url(forResource: fileName, withExtension: ext) + } + +} + +// MARK - Foundation.Bundle + +#if XCODE_BUILD +extension Foundation.Bundle { + + /// Returns resource bundle as a `Bundle`. + /// Requires Xcode copy phase to locate files into `ExecutableName.bundle`; + /// or `ExecutableNameTests.bundle` for test resources + /// + /// Solution found here + /// - https://stackoverflow.com/questions/47177036/use-resources-in-unit-tests-with-swift-package-manager?answertab=votes#tab-top + static var module: Bundle = { + var thisModuleName = "SimpleHttp" + var url = Bundle.main.bundleURL + + for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { + url = bundle.bundleURL.deletingLastPathComponent() + thisModuleName = thisModuleName.appending("Tests") + } + + url = url.appendingPathComponent("\(thisModuleName).bundle") + + guard let bundle = Bundle(url: url) else { + fatalError("Foundation.Bundle.module could not load resource bundle: \(url.path)") + } + + return bundle + }() + +} +#endif diff --git a/Tests/SimpleHTTPTests/URL+fixture.swift b/Tests/SimpleHTTPTests/URL+fixture.swift new file mode 100644 index 0000000..c8c7a14 --- /dev/null +++ b/Tests/SimpleHTTPTests/URL+fixture.swift @@ -0,0 +1,12 @@ +import Foundation + +extension URL { + + enum Images { + + static let swift = URL.fromBundle(fileName: "swift", withExtension: "png")! + static let swiftUI = URL.fromBundle(fileName: "swiftUI", withExtension: "png")! + + } + +}