diff --git a/Core/Sources/Helpers/JSONEncoder+Helper.swift b/Core/Sources/Helpers/JSONEncoder+Helper.swift index f18a7236..08bd4d71 100644 --- a/Core/Sources/Helpers/JSONEncoder+Helper.swift +++ b/Core/Sources/Helpers/JSONEncoder+Helper.swift @@ -8,4 +8,16 @@ public extension JSONEncoder { encoder.outputFormatting = [.withoutEscapingSlashes, .sortedKeys] return encoder } + + static func backup() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.dataEncodingStrategy = .base64 + encoder.keyEncodingStrategy = .convertToSnakeCase + encoder.dateEncodingStrategy = .custom({ date, encoder in + var container = encoder.singleValueContainer() + try container.encode(Int(date.timeIntervalSince1970)) + }) + encoder.outputFormatting = [.withoutEscapingSlashes, .sortedKeys] + return encoder + } } diff --git a/EdgeAgentSDK/Domain/Sources/AnyCodable.swift b/EdgeAgentSDK/Domain/Sources/AnyCodable.swift index 349ea9aa..9d1879c0 100644 --- a/EdgeAgentSDK/Domain/Sources/AnyCodable.swift +++ b/EdgeAgentSDK/Domain/Sources/AnyCodable.swift @@ -52,6 +52,8 @@ extension AnyCodable: Codable { var container = encoder.singleValueContainer() switch self.value { + case is NSNull: + try container.encodeNil() case is Void: try container.encodeNil() case let bool as Bool: diff --git a/EdgeAgentSDK/Domain/Sources/BBs/Pluto.swift b/EdgeAgentSDK/Domain/Sources/BBs/Pluto.swift index 7b48cd3d..82d4a98b 100644 --- a/EdgeAgentSDK/Domain/Sources/BBs/Pluto.swift +++ b/EdgeAgentSDK/Domain/Sources/BBs/Pluto.swift @@ -74,6 +74,13 @@ public protocol Pluto { mediatorDID: DID ) -> AnyPublisher + /// Stores multiple verifiable credentials in the data store. + /// - Parameter credentials: The credentials to store. + /// - Returns: A publisher that completes when the operation finishes. + func storeCredentials( + credentials: [StorableCredential] + ) -> AnyPublisher + /// Stores a verifiable credential in the data store. /// - Parameter credential: The credential to store. /// - Returns: A publisher that completes when the operation finishes. diff --git a/EdgeAgentSDK/Domain/Sources/Models/Message+Codable.swift b/EdgeAgentSDK/Domain/Sources/Models/Message+Codable.swift index c909ed32..8e3823a8 100644 --- a/EdgeAgentSDK/Domain/Sources/Models/Message+Codable.swift +++ b/EdgeAgentSDK/Domain/Sources/Models/Message+Codable.swift @@ -22,16 +22,22 @@ extension Message: Codable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(piuri, forKey: .piuri) - if let dic = try? JSONSerialization.jsonObject(with: body) as? [String: Any] { - try container.encode(AnyCodable(dic), forKey: .body) + if let dic = try? JSONSerialization.jsonObject(with: body) as? [String: Any?] { + var filteredDictionary = dic + for (key, value) in dic { + if value == nil || value is NSNull { + filteredDictionary.removeValue(forKey: key) + } + } + try container.encode(AnyCodable(filteredDictionary), forKey: .body) } else { try container.encode(body, forKey: .body) } - try container.encode(extraHeaders, forKey: .extraHeaders) - try container.encode(createdTime, forKey: .createdTime) - try container.encode(expiresTimePlus, forKey: .expiresTimePlus) - try container.encode(attachments, forKey: .attachments) - try container.encode(ack, forKey: .ack) + try container.encodeIfPresent(extraHeaders, forKey: .extraHeaders) + try container.encodeIfPresent(createdTime, forKey: .createdTime) + try container.encodeIfPresent(expiresTimePlus, forKey: .expiresTimePlus) + try container.encodeIfPresent(attachments, forKey: .attachments) + try container.encodeIfPresent(ack, forKey: .ack) try from.map { try container.encode($0.string, forKey: .from) } try to.map { try container.encode($0.string, forKey: .to) } try fromPrior.map { try container.encode($0, forKey: .fromPrior) } @@ -48,6 +54,7 @@ extension Message: Codable { if let bodyCodable = try? container.decodeIfPresent(AnyCodable.self, forKey: .body), (bodyCodable.value is [String: Any] || bodyCodable.value is [String]), + JSONSerialization.isValidJSONObject(bodyCodable.value), let bodyData = try? JSONSerialization.data(withJSONObject: bodyCodable.value) { body = bodyData diff --git a/EdgeAgentSDK/Domain/Sources/Models/Message.swift b/EdgeAgentSDK/Domain/Sources/Models/Message.swift index acfcb886..417d6363 100644 --- a/EdgeAgentSDK/Domain/Sources/Models/Message.swift +++ b/EdgeAgentSDK/Domain/Sources/Models/Message.swift @@ -3,9 +3,9 @@ import Foundation /// The `Message` struct represents a DIDComm message, which is used for secure, decentralized communication in the Atala PRISM architecture. A `Message` object includes information about the sender, recipient, message body, and other metadata. `Message` objects are typically exchanged between DID controllers using the `Mercury` building block. public struct Message: Identifiable, Hashable { /// The direction of the message (sent or received). - public enum Direction: String, Codable { - case sent - case received + public enum Direction: Int, Codable { + case sent = 0 + case received = 1 } /// The unique identifier of the message. diff --git a/EdgeAgentSDK/Domain/Sources/Models/MessageAttachment.swift b/EdgeAgentSDK/Domain/Sources/Models/MessageAttachment.swift index cd61d309..27b6d727 100644 --- a/EdgeAgentSDK/Domain/Sources/Models/MessageAttachment.swift +++ b/EdgeAgentSDK/Domain/Sources/Models/MessageAttachment.swift @@ -178,13 +178,13 @@ extension AttachmentDescriptor: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) - try container.encode(mediaType, forKey: .mediaType) + try container.encodeIfPresent(mediaType, forKey: .mediaType) try container.encode(data, forKey: .data) - try container.encode(filename, forKey: .filename) - try container.encode(format, forKey: .format) - try container.encode(lastmodTime, forKey: .lastmodTime) - try container.encode(byteCount, forKey: .byteCount) - try container.encode(description, forKey: .description) + try container.encodeIfPresent(filename, forKey: .filename) + try container.encodeIfPresent(format, forKey: .format) + try container.encodeIfPresent(lastmodTime, forKey: .lastmodTime) + try container.encodeIfPresent(byteCount, forKey: .byteCount) + try container.encodeIfPresent(description, forKey: .description) } public init(from decoder: Decoder) throws { diff --git a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift index 63d16a2d..e7b227b3 100644 --- a/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift +++ b/EdgeAgentSDK/EdgeAgent/Sources/EdgeAgent+Backup.swift @@ -120,8 +120,6 @@ extension EdgeAgent { let backupData = try JWE(compactString: encrypted) .decrypt(recipientKey: try JSONDecoder.didComm().decode(JSONWebKey.JWK.self, from: jwk)) - print(try backupData.tryToString()) - let backup = try JSONDecoder.didComm().decode(Backup.self, from: backupData) try await recoverDidsWithKeys(dids: backup.dids, keys: backup.keys) @@ -208,26 +206,24 @@ extension EdgeAgent { } func recoverMessages(messages: [String]) async throws { - let messages = try messages.compactMap { messageStr -> (Message, Message.Direction)? in + let messages = messages.compactMap { messageStr -> (Message, Message.Direction)? in guard - let messageData = Data(base64URLEncoded: messageStr) + let messageData = Data(base64URLEncoded: messageStr), + let message = try? JSONDecoder.didComm().decode(Message.self, from: messageData) else { return nil } - let message = try JSONDecoder.didComm().decode(Message.self, from: messageData) return (message, message.direction) } - try await pluto.storeMessages(messages: messages) - .first() - .await() + try await pluto.storeMessages(messages: messages).first().await() } func recoverCredentials(credentials: [Backup.Credential]) async throws { let downloader = DownloadDataWithResolver(castor: castor) let pollux = self.pollux - return try await credentials + let storableCredentials = try await credentials .asyncCompactMap { bakCredential -> StorableCredential? in guard let data = Data(base64URLEncoded: bakCredential.data) @@ -243,9 +239,7 @@ extension EdgeAgent { ] ).storable } - .asyncForEach { [weak self] in - try await self?.pluto.storeCredential(credential: $0).first().await() - } + try await self.pluto.storeCredentials(credentials: storableCredentials).first().await() } func recoverMediators(mediators: [Backup.Mediator]) async throws { @@ -330,7 +324,7 @@ extension EdgeAgent { .first() .await() .compactMap { - try JSONEncoder.didComm().encode($0).base64UrlEncodedString() + return try JSONEncoder.backup().encode($0).base64UrlEncodedString() } } diff --git a/EdgeAgentSDK/EdgeAgent/Sources/Protocols/PrismOnboarding/PrismOnboardingInvitation.swift b/EdgeAgentSDK/EdgeAgent/Sources/Protocols/PrismOnboarding/PrismOnboardingInvitation.swift index 8a0887f1..c85772bb 100644 --- a/EdgeAgentSDK/EdgeAgent/Sources/Protocols/PrismOnboarding/PrismOnboardingInvitation.swift +++ b/EdgeAgentSDK/EdgeAgent/Sources/Protocols/PrismOnboarding/PrismOnboardingInvitation.swift @@ -10,7 +10,9 @@ struct PrismOnboardingInvitation { let body: Body init(jsonString: String) throws { - guard let jsonData = jsonString.data(using: .utf8) else { throw EdgeAgentError.invitationIsInvalidError } + guard let jsonData = jsonString.data(using: .utf8) else { + throw EdgeAgentError.invitationIsInvalidError + } let object = try JSONDecoder.didComm().decode(Body.self, from: jsonData) guard object.type == ProtocolTypes.prismOnboarding.rawValue else { throw EdgeAgentError.unknownInvitationTypeError diff --git a/EdgeAgentSDK/EdgeAgent/Tests/BackupWalletTests.swift b/EdgeAgentSDK/EdgeAgent/Tests/BackupWalletTests.swift index 41eee5fa..af4f4931 100644 --- a/EdgeAgentSDK/EdgeAgent/Tests/BackupWalletTests.swift +++ b/EdgeAgentSDK/EdgeAgent/Tests/BackupWalletTests.swift @@ -98,24 +98,26 @@ final class BackupWalletTests: XCTestCase { XCTAssertNotNil(linkSecret) } - func testInteroperabilityKotlinSDK() async throws { - let jweByOtherSDK = "eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6Ii1MY3d4YTFWaEN2SGFCYkRsUllOS09TZWhOUDZwa3VWZzBfVjhmV2pBbjgifSwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0.ZE4CDDJsEVwULOfMcdzzNaxGznu9ZoJMkmKnao7bA8Z2sAm61pJYCf4NgDsucY-AxMEEYg_8cZjWiBkwDjs81xMfz0xqvr_B.E4CE9ksIbhc-WRKwz6qT1Q.rO2bqiFj-gRc2CFM9t5sRLfnNZutmN8b5TTRgX26oxjS-2IRRSaS8vlulGCAUWF_zLw2P8xXzeVEJ8egE_c2ikr9nSeQvwcVNhr1iAApj7-RcTTtwKYa14WDtCrU6uX-USbiTtbCrWrhuVSnTFpwAwO03mGk3WUrdOcOUYeURzbX5hcnPzsYxQCxo_Z5r-yx-dhgjyHQa98ELOJRTGAOg9gdM1-IGVUfRmYrPxMop-v2Xf1mn9MGmfRJ8n7xKxXsK_kp58btzkz2K699lxblOTDUumlGR4k62yEabQVartL9DdOM1ncfdgVdJMdVb4rZz3ujnI01u1U_PLh-z9YOg7JaGNChZw13quzUicFGGmnme7n3zjMneMEH7rIrb_iakH8IlEoNZTsnQ3kXQvA69-n-pn8a9fc4GNnbgkBMK1uC54Dei7icOW-_AA-ef154EOakOa9E9LdxLMfufCwlsuTHmuS_Czy9ie3Z1BfQc-_rbVAlN5zRbyg4y33GXG3lu7Mdqn_-lY0oKKYg6hkWAVIpVu5GYdIQv834Myg7dcu8XXeM_tne0ngk7KY21gy0u-cse7fL8lETs3baeKr3He57bOJB0ZorpegiCkvPhelr83mOYXZHf5VDE0TiWDTOUXart1bGHmdpgH5AbXCNxawJvoUqQBhtsfgPFh_nSToUGDMxR7DnY_poVYblCeHiXodOuNSy4Ojyw9FPMLhSXYw-UusPmjUmC0YeFlH_w6NFgS_liZURkX9VR4soHuSGoKWZHjAOinD1orMh5UTLvg037p6nq2XPVEjq2kR0eZ2YanE0sjs72YCGFTc6Fa5hhaYmhFJb8Up9756zyeehtNU8Hsk3kHI_2bDfWofOGu0bCRxGotx-JmS-HGjGwDrsFLGofqEro-6hA9EKlQ07L4K9ZxmY4QemiHXexAnvaKjiqkYUoIz-KmThnynu1eYCGRxxmDQgkFYMZhSV6GYbmp9VDXkfAXqDTAOi6RtsVX5BSb-LD0FCGLKRiYm6u0TVRX6pDJhpxJxvJtH6Hc3TpgR0BC2C4GzOW42HwWFx-7LsDoZkdgMRczKR3sNDvVuPMSmJC7wPrn15DwqEDLOi2mrZ77mx9IwYdQh1oLKIzXjtP2tPxr_8xg5Adah2bemjgUQwBpB1RwN5udFCrU8sZUEVBL19TPgsMfdR9OTMFO4ZG34VaK8xRh93hm6b1f2hOeT2zogmPQIfCswa3VlvF9QvvpsF7eqmvFk2QSM_jEjQGKRP9ydNB0U-Aicg9e5iKqdp_E0ggVW-ztj_h74TtW7g1lw0zGOqt79MAYt83LL8imGwEdrNWtiWKCSZ_qV6XEAS9uMFW1JR8mpJwPjbVA7VZDS9aqG0Y_Ei-8JBjX_0GPk85IFVw7d0qiBLEeYJ7WmkB732rEa77MeV-29TblWujyaCH7Eea-PtsneimPawBZyBJqPUmEnTf1Qf7dlUcyl6_M7TSIcwVOF-8_-WUx6-h8cCX61q0kDiAago-oT09uvbjiLSuPauuQrBTpHSVtEsVjRxwFnCRMuNTP6t7Scp6tsqxfbPVGO7iVAqK_0L85P8a3Xp6rVCe40AL2EKPQE4X6JKKbHqnc6ecCyOkDxB72xRncTTei4apg0jH2UaoRTSJw8owpasfOQBZs6drmt72bkRJ0RreLd4SqqPRpWK_BeVG_dFus57BnkQAmDxkMBGKG5bhZOwUF22orHxkye72dMSOOFEX12Jtvd9vP4YODepL49s76OiLeyAMK7ws0xbqLKQxWjHJlYTksgTl8UNDFG8oEIZKNVvDUQiEbKWlA_2Gvq7yovahRhMVMbnQ2xK9kw7hl_Vi0M77ctNIbRR8UmfL-o7BsTHEx2mVVzAUVKAgPcDbAKXkSI8DjlP9eotp6-d3Xd4m4JO0igY09tFpCtMu0CWNinOc9-GcwPZhQhfBJBXinYjRlwLOaGZWjLrPHQcuMuaCicf4t3yXWCvRXx5INSR-l75zANPMdpHvAVH3qgXMaLk5xMH8Vb3KOdJW4c-3DCLqL00juohZEguOx4grhYa16CRLXqsN-QcNUdRkpgHoQXudBL702WtLTS0CAG29MLMKYQIbmSV75KjLda2ieJA3OzNAPWoITy_wJlRPJzN83eFqhr7AmmXK9Kx2JUV9laSlZeJMm2emogLbEftKpGdc9mGyYsgmf0a-1hREi0kO-m0NxVlHuhCwZDRYL3CpZ9IMmhyc2zbMWZ_HK4w3MtEVBleNaHTuBZyQxAWSx7RmZBljQtIyTERC-m6eJp__WsEFUqe2qTKUZWZ2rXeBjipZA3GjOFv4oSDr2KtgulfShpdK8QmpiWWzv_YM6UNUEHkG8sWiXYbyuK3mTuSW0A3tD9B2bfjZKwaAbAVIICt07dsB3qr0eRiSTbJXiW0WtYONCXCs1OYzguUOArS26iicMaNRJQdiLWa8dG5f1yX8C6_CHkT6KXJobzKbruvbPBt69s3I7ihZ3f-ngnNzY6Pg_Kvp8Wj4lvMF0CiNHLAEsriBQoX6xp8LSSbBow9iRTiSChbazfAwY3IN3_324I4QU25kkIimROECZcjV6SWaoF0EaKtivqRdm2QWJDxzPKtU8KGwyfkt10xu4mugqOFkKadATw1KMML4bKJHJ2i29X0hZxiahpInsY_tWeHYYeQr84R-3Y3wQPItFt9KTR_3JZE2UsKsAKXMmen07Ysr_cVF_uKc4-B3FljomjkBoEAbwPPf3gT5r9Nl7uW53zdJJV71BidVw93HRzTSR4141RHlO-qyay6DlAyta2RIcaQXmguS1tBE7SqbayZRNVzQziiijnoH1sSP1gJ5TPeuB5qXj0zIK3pkOTA1vVDPg5X9Gid8gZIhKGwJxI40c5j5L2gQt5M0b04QH8t_cy_7dLJH4IrlOAbgUZ1ZNx4igsG_yMmvvDFjpsshdrJATcAqgVVeVH_WFKfGK_h3x9P4NTPcEEzuxD2RRl3dK22WXxBaPFCTwH3YMTbE4wIMRvc2g3prYWPVReLt7lXf4_dKcTsN-G4LYyDIwWBNklBnu5dNYh69h4Bwe4441CIf4_HOigzCtkQfZzsMN1fVeWfTF5RLQgl1IuAXEQ5hvzDqtMWt62gFOUDT7CsR2GaEjWLkNrCChwMATOcq-9ArsxNW7IE_YCy_O29GFvqMfK6WMgJjSu4kiNmXdRercXlkePobu63WDfjRwANvX-aQ20_0rWVdbv-WMsI-ZItXi83IIqhR4hZSY5r5rAzRswzduqWVfQtOhBG24uyKmO3RE47E7lCCI2gahj92RwqlAnhEzwLH-Sqa2_lDVHHD0Eg2NvJTtuW2NhzDviQaVNnYTeg9q90WwS53D1Ef-eMHxwoQT9KmGKDwx3hMfaWAOpe9lQtFFJn44v2alBcidQovc-r2XDhIHEzEhtDHnkqz0AIIhD-LLX_0q5-pZkAxvCSGlrIJUPghAdp4BsaQTT6QOPPxYpS2bHerhFMyRUZ2-R70lrTqAPsd_7mgBQAFV4lbnwzLvg2tKV8UgqIYIplqZt5Aa1MBl26aMStf4vqWnSxbl0MSgzaK2JpGw-6yrOap_E30q6C5bGheP5uYHdbRbxTS9buAv6RTtR9VE7q5cxFpycYtYH_hQCnYLTpGCwOZIEV6v_f_U9TvzPyx3FXEZIFfFY2NcfvVLkVNtphkpgOubSx78e84Swsu6GVgGGTP4I_2qIZuYVjZ3dKcn6qezgs2sdMgEKowe3_AaqE5TulAkFat7CIc-ln7TkMg8fHNLxWa-u4xXxB-fB181qPdEHG4-7fPCLkSV7oQfupPXEQKYi64xGaj8wte_eyYGQsQvIRKLw93YNxlwgVmUrW-6Gbc70Yj-dxRWC6agXpkJS062l96y1ZB34VF-djefjez7cqTYRbWlE4q1qRPpcvjXWESjtq-LlkVMXuj_WvONlZh1j3V8Y4e2bqIzIHpi-IjpsFgefnffMs8zkGwPNUB0c_BxrDqR8VbWS8XDf2gq8q8qg_BuQhPSxzqmKLEz6kLTgnJW2_6PALjH0LVBVNkoPteqq1Vbd_yAKBe77PB69Or28VOglbdWfUZczgYqL0HHX9mA6RNDlhSflXsx6tc5fqBSsO2kOAjC1kLYxi4O1jy7_jwizbdv1epjrnB13rAzfQAtOGrHSBOokz9yXQwyhkPiD4mz7rn1msqbprAkVnZXJU7HhtxVEXEUoqAKYnu7C9g6jM3GAXYcb92OD_wTx99-Le5q9mL8mpddzHIKxX5dmWnZ4lNq2ureILmxz3o841FElHYmFGnkVwSpxLvWnBDv9XbCPYT_HEC9iSKIbXO6giZ3ugJMzZ8SVTltGh-HFFTevN_PHRkE4ACYzRtn1DbyHFp21g2begdy8KATsbuhhCh70tfDK2LKDj0nsrcbrn0AMJQ9V75Ermzd3jawOEq4It4R29IEZZyjl9gU5XBFk8R1lfDjXY1tjpWacXDWiMq2rOrqy9B_a-GQdcjnpDPxakz0ItxdMAE6HpwINLv4Fs-AI2UU2j91XseHDyN1edLI80h7YK_xnvxFKyfta1OdeRvFX9YhYrbmhwUdtMH_zedwd_U20K7ZDjMWY8ZwwCP3mgI-LmDxhXU8Gb-eL2gXeyy1L0Z_G5DCQvSoOOJjctt965YueSVdamMZJTtvJr6EqQt9f_6KqsA57xrVeFBLP6ivdxLPE1J8RUclfTxgSHqEC9OP_z7AptLSGgnBv6wtdogymMgnZ43LbhqSR4B28ZhB66YGsDRD7GWyphImqE64U4dnJjGb1OJksTIY0U_npMSR5CT_3cL0YgR3z2wxr8-U_KWDNnfUR2DyW336rOM68PvWu37Hq16ckUuRmowr4xHX6N1NTBoNYE8VKCl4t8kPIVwIDdP74HB_mvh4gLtG1rTgSz-Uv6EOv7OFB1qFkHgx025gcsXYgbftPkOExuVdYIs6QrJ9E4-gVu6RVqVr_EHovKwngg_yZn_sjWdweIXQ7zBlbzrOHWCah7CEbMaUeGNU72rDW7sd_ljmSfAz_c0wFTLyJmjGs025MiI0VW6wnk3suFW7aNQz2qkrOLujaxg1Bscxbku-KYCsFcY9f28RTikyU-GzXCYLXYz_zuD2ze41K9Y7QNXLd0uFltcFqOA81mJWDmWXfuoZxqvZ3ICZt0uywLtX-KhgvCQ20AbRsjgpnOq--CfKMkT80-TWDOL6X0rHqcxIfcYflcz_lil_Swug7HpEp7wBVge8pur1ejYN5X7OqNc5OIksOwlRWYjg5DF0h0XAPzfQ-UcILpgNHVmyn2ylMf9MeOiB1IoR0IErOx7g86Ms6lc9uRGsdVAbrcOp0SNIknyvUEprllGg--2Um2tBZSOX8mjf4UdjWxxkDu9X-42s3yo4uONRqii2oQIeQfsmNsql0-t7Mz1cvNmK4mijIM80n8ZbuiUO0UPkZ4A8905B_DPA5ZJk2Nal7NIfUIWwI1Sl-PUfzMYmdcKDsI2wl5LiN9QjoiN5UP4kjlUwrKWa2ecpLy-64WnDev-8cSmmlLIDyFGVj_v0gjN_eFDL3nzuL1oLpU-BqgFgNXY637saZG_yiREufdZi6CaURJ0vmKrf5NAaqg-tw_gfTWCTecGiWNmTeVThG3D03xOTBPsBh5ijYLwUXE_GQZ_8Tu3U1N3kVCq7fj8byMtfG8Xdf0ik8cVcmbsTnrWDSFsxKT0lUY2ujsXqEs_QUMS8n98PdrglrxK2xeO0umzD-FHZB6r4-0hNMXgEd9dHUmBHSKAznO4kEtUoWieKlPVomTfjyR1P5hY8mUbpHMbS7QUH5kb96DbzUGrn-Y0UK3kpo7KKyGizQjpwAiJykNTWJDIsZDYZMKHs6TkMU0dHMrtsfFY-opQiA2wHLowWB7RKNTIgUSsZEOyjghkel8nHtoOubiqjmwt9ZcZN_2KhlNZ76sCvZAEXRvDFrF2vquUREBM-Tr3FWaXuvFec234dgVbRAk-xUmcQw371smDN9iEWFKTUGzrzS-_p2E1sKO0IdWCGQk6lMFtNdoE4P1O-5UJGJP-0x7nEigqPUzr4ONevZ6cD3HLCOFhmFhxPaC7_q39WPIhXj3NYS1ZVEXo3S0exka5J9AJczNv_oZmCYC2TkqLw3Z2ind-gP2x_cKIVMswMrQ3S7caYXCExFSKzc6rRiDjd7q1GlOa5LJhlbNbWl07ifcYsQIWCiw-0BhbjMtJwjbTHJggjHtkpernO05Qi6zPlKK2Kj6AqRKQOflYR3DLxcoAjpAP9xzIbf41nZMr7Uz3PHcRwinj_cMrNzHtik9qE7S3tx7lnv_xU-eogiVid1W0giKrlWzvaWePdIxRkF40VE25oMAJBaKpYPhQy0wNMHfNLuwI0MX-4VHWIVg-xtjEhIXwWGXOI_2bAItsk5WBjit6vZluQm34XLVh3_FQN72sST2rFSN7IgV4CfFLIimXHYchG37uPpV6kz13sTyJhqRsx9FQOMr5Q4ALBX5hQeQffwl5uAoF9ry9T1MzxrHyDLC-Fhq87V8gbEcRbJDq2gvrowp_qouzLmhs5L2L5YQBxRnxGh_tTcJk4jse5Nik535TCI4k_lCPHqiZL5eBZD5nvUcGA2d0K_Ay1OabP7ZDjf_7M8jwSWg9SCw8_tG9bxmOKZ5w6HlhpCw8AFk6OkenlSF6UByT_hDupcZeXiis4d_Vvt8P28sHYkK51mXPS6eT7k_7d0WKDa0l5my-0n57PPtLS3I4LdjalaSNcTGAEjcYUSAHaCUHJRwhVQ_zqeiGhT1aywZX_Mq9C7BTn4GmDxefyxvDdb4oQyJ42leRTCzhOWhQzLdzwqsfLNicRI6tQK7GK0U0LkOwmVxqRvUfel4fQOAweGXeZFJX4aRtmsWc5vW6c7GIdNfNqX0gi9XYCiWCWXKMS1cqaLggLqOFXyWcLPSBdv2A0M5VdUGS84KfJ1vpp1oDH5K1y9Z1kH1lBtYFoXvYbRhHRTU9baRLsQ8lp2VLZFBP6owQrtmTs8NLet08b4QyQrzwst0x4rkV6vuAb908L0Kk1v0KMrMKP0RYMz6CiC7sF3Dn4U3Lj_MVuZQs2ox2XwuAXwAlCXHTp_0hRzPZE9MnoIc2k8vefin0k-7UITeo9l0Dmnpo6n3WR6_reSj7dZuH2GYwKW84xGoy8XXtsdpWCXgCHDXK8Nv7SOu0UgbImvpUAM7EmFSK4yRGN1jJxwiIJAdVDrRIiWMEpjRWc_jCB4-iPhREkfJjQVsxl8wJ-GNQnct1e9aDu6VFRhjM7FYZSf20gtu-XiQBShP8Qo1Xh73Nbw9tk6Qil94yKFFh3LgAeyVPV2ngPEKB417RdVqVYJ6c18srydjjW8J9VtqtKla8hgs89lOnhLVu2-0JEOwRYRCR4gL8qkMqis4_HM__i4k2VuNSMxdBTrOO2OIJL6osPymgRbtVhHNa3u69TdZU0WoXKvXNpylEz7wuGTtcqBJTX8ONXpBWoFPLK8i8Iku4o5R5OuQ2H_wNadPyPV0cHVMukgt4HtBGmBXWsAqJTB-jwxFHZf3UTVbCxkdD49rvm04rGE_PDR8kltMF6jLQb9Mq8LgDV9U7-kDiGeT708Iw6q-4uasKV_B77hpIst3BmX0qwdVA0OyPhAm9WKqkWmn9zVOCKZ4CWkoOeg7YVHGoxm1FMtchmZ8eyml5ZN-d9gibQf-x8IvGIlhatX8xAPnN7DHL2ZFVI0fJhJoncMe56bTVIugf4CGdayqIySkxfbAcEij_6wIEr7RVPMTtsLSM8KujTncVvUpQqjjQ_XTZGdB9ANGnZWfoV6cZ8V357aSwCYDWPu8OPmgqlcbSAjffdEb3qIoDiBM5yEnqNijq6CIwSXe9bjsT50F4NiKedczqs59s1UmMcNXOHzCdZBdMxudYqoLtgvDWtMuhd-qVOh_w2UMj_TGIlzX7-a68IYriSjswBScwof2jRuaUL1Ty51V9crBTLYyQoMw3tb2HORVhYXDnZ78pxMdEdoT6sP4eqigvRfGsqMSdBN5eXxRxMJl449Uzlv43OigBNJnpTgIPmJUepcY_4LvSCxTKHpxEtdTIw4HtDptbyhdpy79zXg-eEGyGAU-W5-Dq_wi-uhWPUoDEPaAlReB6y-8HmRVc4GchA5E0SQlodEiXo8XXIgpfUc80HhaIDqU-rqu7e40_3ysvBlgjAAynvlHEyizbaoxGvMFx1VAJ_Hz6WdP0AHrnx6zanWO5sANX7MXIkdJeKMZnxGyFhQ97Z-BYn5fIitU6foWp6vEVN5fzjGrurxhuo6rmsoSbKD2TQ5sYf-lmq4NksjcW5Hp_HHQO9amsnmkwSg7_do5vySETNqC5Ib9k8Q39Ck1oWD2ZlLcNWpUFDauVZogWh0vf1M1yQkxkqFxOZFyhJzfng8CXnetc-6kTIw7lMz-qtUqL9DPHMCp5yKz1McUzq_3n3qvqiI6mr0nueqdsjlI04qssxpm_bBgYkAayaI9L8GdjDzVSTiwAapiXqH5JeyIpdt47IICqIIN7RqPOKD6YZkIraTDppUgemG-MUCtyrBESnQ6mRVVjaWGy1xUOKhiNFuCYVxQ_BrGaWkHTSOHAIEUDPdmZ0pMrgzaAszbScb9GKR4dEblyn4sINNN6XqJTNPgkhrn33AcX45OUOsByNk5cxfM3FgW4-KZU-6LV9j-yrKizAxDPHAWEeR-j6nThKMyMAtt94p3dVh1t9-JmBAP-NYKsEoQoH5AWA6cJt0mvSmQO81_yXigLVmOJ4QhEyszq5tuv5WdfLb87XGrRQOc0Wr_e5CQ3awyk-eSNpPPxsy5IHI-Wgur6SojHVw4iKEjipMC_neRGOx8VIs5tTVW7eyqkBZ4B7Q7r2cFyF4pMIUYnd4UcfSFB1Be5PEnkLL3TUkRQCqMU5hapS6F1DMWqgVhAWBtnDLu2zc-tsZqMa9IC-crfbRmAQiJ8s5kWBEhxIaIjPz8o0Jv-Qj6tjmQN4RKTm5RkMx1hUhwBsBZ486YMhtSu7zbeeL37rnFm2ELZoJiSHpn9P3w7VJ_zirMaE9139Y895hyGoM6t8Sbie11Nn9wTczbTotTe4AgJa7U1PECk1CnrSI-xJyE1GXzcxuljsu4g-6T3vJ8vVbmGbD4mbJiFJ3_Z2LNlu7uMiAR1zY6_SSgcxjOzIS-oGpCrz4vUw-bDGaUzFjqsdUtbkXklAvGoX_Rns5DdgaNNm4uVPeKVllR4eahLbbzK390nV2fQ3A65Habnw4MfVoVtpnF5-TWT-MHfxmZI5SHA1IU2vfzUN-jIlKy29qD4usUGCU2qCPbArOF9WUhSjnRYlxaIVWTWW4RVlT3h9IdCe4l7zGO2LBgCDetKBcKJDd2v7E4JCu6dtFvnDmwCcjGJlDXNw-55OqFaPuULW24lQfTZhZllT8wO8rqvyHH04yE8FqOPkx1nA9yiaHDbCqB0-JuJkrXPzcejUkX4-J6-uytiSGHfFz5Oc-LTVOWiQna3wMZ2Vmu9knGIPwTsuhrdd0UNZNDmv52YmGl8JwT8K2VnbbLvg-9Gf81849dzyMm6azxg2nUsEvDYZc9wlKTVoceuMj8fKFi9Ge_1cqiM4PeInHB7ZJXBZwkytk0T-gWaCD2hM--KfqCwgKxF6f0o9oTP24yDoQoeXxhcVeC4qNdfk_MRPeuBkls_XSnqKTQ-KlYjTaJvrNoCvZwRoGH8KYCtzXG1CIye6zJA06m1XIS6-wTzhrPd_K65myY9rRuz6TqhGj8lpHVPE4wQNU8QvSYdEHAF2leVW27O5s93AV3YrEpxdWkf7wNcd-p5MJeZCmqfo5S19zX_w1fmOSmOCqO90NbJGiCvrSbWKggoeSdZUbpKlec77wpNaHxJldrs-FoL89XP8_8A7zAykx7lh5-8L32-eHx6lTwqjVECdTIpwCgfP9BX3LjJOp527dIxSSLaIcq-lI3SekCPnexI0gENj_AhJPeC6b_Y5agd51nbtBMkrL2ZNspy7MRJY2ioc0q2J2s0HMeS577r16Hb_ZCXiK41D19ZRSvNU7nvRM1SQx2A2kBWurBg6rbxIimbxU2F-j2RKTSdeNZ5R1ou97nw8y-tHWNP9bV_3deSFAln68M--sYKAjFiXgd_V8x5hlw0rBvBgAReIzhvrBmGnm9bIwdvUott6ScBL4O5cbB7hM3btkdt5PPKXjnb7mwniuZpdBjrHY5CYE0o_Pvq1ZmARJyUpUT_tglWvLizAt0ZeoerdkqXF7a0S7P89QiiE1FhnxjB63sd452hFp1K4relEDhQvSEXO6SUW6zx0hIp6Kz0VhN4sKeC-eDv0guD7fOSN-ZdiQ7UYHzMGO4P3HjeD44ND_hRVGSL-08GV_xd4e9jyOaIE-DlmcgEvzbUpVBDmfkBSJZSfppvrv9Z4kREpk2ZqDM8TmchmOLOG9ETa4B26F8RnSFHsDY0hK9x3k9jJ8xjo_kZmYy9fahofF3AzTOZqv8HXkCwbSDjefQrmgI4Asd0DnjZE3kwcPiuJMsvSfil6gs47orqn9WHU4OuWjz5RWj4pu64-urxmI7N_slqyjgxXVPMtxxD2kVSuuK-QV9YLUD_Jyi2vRVdWuinjEfONszOnOk9ujzLPllfd9vwM-Cq2txzt3-5L9mCSBfbDLvQGVpx36QXCM22AHpQ5ZJHmKNleLVCeYC3QLi5ZMB3LHxg4bDLYuFgzjxG1xlci83R_jDUH9WZWowMNHqiGvc0xODbW221z0pjztlPL9mq_w_D-_N5D3-zaURY4WNxp4301C4zIKqS540TpOkEh8VrgUxOBWpx20AMbyi41SzaOpIPnUxVHXrRSzuPNIY7ULs59E9jkq05hMEiyRfvQ2iTuoHqEJrABkotTeZCztwPCuGQSCUbXtKqgZqQxWAusqGYi7eXoZDVTRY6ctDpGp65bverjObJAt2ftlqnpEpKO6Z79NkP6FAVqZ1Xi9UwpNwbPbTD7btt8OuO0iYfq9isDKPEqLYa9STg2yQWiIGusBpA1bO4lMZok-f4hd72H85GhqdB57F5K_vrfZ7NYUsLxkfQ_XTSaVfbPY_mZxSh3HV8G72EWIs7kii4Qiema_0BrxbrDLchqmig0hMfTQeQOYqEVBd548gxBjXSraRXafrELKvuJpNlcd9yAkP3wIJBGLjT1a5H3D-J_wdMNvI0bXlOYx2Mg9O_vrpaKGqQV8-i7ZgB_koms2K-WTlXCWzgHrSaLT4C-Adl3vCdDBD0TeXmvuZSc9QjWqhCr_1zUdCwEyNfcYNWtj5RXSm-RE7qWcnDU-f7N-tu5sx1sv9NQJkUidyI8SH0Y7kR9QrdqGXA7TxvnbogxfVdPM0XTV9KPPQwJGsyCIJMsUb9IX6mZFVMGmfC9qX85l_VMQaPvQzlrlXHJ9fOPKR6MCVI7Sornl-LsEOnX8dOnvYQHIxzYK2GdAm8asIQcEAiUZfVPuJQwt6jtaGuc9XD-9N3pPxL2uP1aiMLo7rC0xmy2iiZu5viaPKmGTUpEAjo4yGTJBH_MEjtpDR38y8OnWQz2ORCca_ewyhtMap0WYbh-gZHk0NggSqjV_uG5L6AgftloyzIG26vp5LIEi9l6MIUWdjYAOGEB7nVNDgmNPHtD49HTnTvK7-M2BtnJnWXwXuA8pt_coXXHCQqQJYJJxQeSX5lDAy5JKBUfPkI3jHHbQon-MJ58Np0o1O0f5SAbUHMftUZh4b_ToB2UOEDjHKTCFGLEnlRPIs7qjdkkhT9wE_oFfBSSrZ3-pOqc-vVvwbE4VoeL2OEeVueP21ClEou9EValxpL1LCvRjCiYgyR8L6DEPYd_9zC6H-7bKf9zwjLzdp4hL7cUqJSWgYHYn-ln0fI4YAvzCwuC8xO9tZ45QMTxnY26h671DR5ZlVh0Ie_CRNsugUSK0WOH2frwJxko8PFRAi84Q_UZt_RkcovyOcgqyxZ7B0U2vQOUMRUT0UDpeperWKZMKQ1_lu6TUevcb91136SA1IXVIxBlYEak9f-xc4jn7MmgzfetXTTkKUM634XQJgpq2T1ehqC07RKT6d7XjCJzR0Q3B6GMrnTHUw8owpdTVuTupoA6R5krArvTjO8K3-a-WYHffu0Xz97Uqg5EHe4tyLVhW5Hbsacne6zW6Vb1_mbdNIk-bB-Oskg_Crt9ba633sT6trWekVlMx5w8H3wJDGWNaU7JeCL1i-BdRXqtTk8lp6e2OU_Qwig3-ebnT49KuAOcJlfIrdXxu6KEk8NGxcNiVDPlyfhCeVqzoNOGvzDkcaYzbhCQNcwCopLBX-V1wGqGz7dj7hmgeFNY6SaC475aFwZRyvsPu3j-cjyZu-XSqyaZXkcM1njXfWxDuZOuTjdzhE4jBXbiwu7PCc4Q2D5EwgY9afZX1QpgUAbQmNiw8a3sr9hdpG_25LpOdPXDYXorjzV4dGp-0SPIuRM5iYa_QGfBYbsEGhGbmfa7Jhku2qkb9m5b-z3Km4hdeszrhwl9hl1IqtjNbt7qh-IWtByHZoxM_XSIgddxchCkXbbbf_JZArRTDpzah1Wj4LOXtqzO_JmMA55Rpu1kJPVauh6qrK4wBeZIocQvOaQKKevWgszupnlZgUXf4QRxlSH-PbMysk7aovFa2Z2lMecEz7s2DHx3pe-JQ7ArnSmALmOWe5qWCPTePsHoIyAjsJ5yp1MCKSMdwKJ8v2KSpUfbs-PVtTaaWPWOYYTF5iaqmnLD7lMbf7FyDPGFJtOE7tT7KyWtkVwiqr6S0QuVoU3x7hOKJTdSu-wmWjr-Gd1I-FinT2KCZVAS4hzxxcTNQkeHU9qN7UprZxL6gol_bJ3Zh4pvJT3Zdrtl6IsdxJ41pmhLMtJK9XgxeX990YQR6HMlzgg-pCjoEdv596HfGfOEjp1U0IQBZNf3fVe5dvrLf81ch0Fe4hJH5yY-CvEmxlTF7JuPoixFxrJEkVuHnct_xjicy-aQBFtszZDxMOOdIvKWETtnuLd-ItqFjvKPqf4CunESaVuOyq_TzVjvWeNnEcC1LkUE_kiJDsNnfAYuYa8BlvtqUKu9mul5lXFaKLDnexBAzSgkqRqY9gBpiYaeOqoqLZNiOFkkRDPGJ34mOGOEGx2n1eBTui342ihh1FT4QbDse2CPAZZR_0CN5bIvhpIXjWxE7zVY-JG5Dby9VaoD_04PpPW2C1AfkLVFE-nQBIhaKwzdYq7yQPYi9n21WBOcv9WdqsfZID4XM6Oc1lJAv2r9Pd2DtmD1DWi4knGUBtYLiwPnBqCkyqDO_Dygr6JYpnH5E5Se8bGH4kZl_NgsjTiVOSimx0mjniKgUF1y5ymZwtet3f2DYtcZZyHxyqpEHmD3ge4ZtORU1Qj66Y6honsOugwBvlABWGlmRMtVsoWQFP9VtwTkQcgi3rSLkfrlsrcS3VMeYJ-QHh3nV1XmDVTZ93SoMB2tozpTlxFVmCq4GZnKNoN-5Cc5MClX1n79IZktHZ2ePSnzwiicm-MYBaUaylt6M7hzxnaz_MELjXHlLdP4ObO0yIO1kgQtGz8Fbwq0zeSVvmZLOziCeX2UGS1dYb3IObdamp5YGOi73JthKfhheceOwBrnvotmk93hSVtvFdG4EJvnlra_gl4YK65nJJjBs01vIdqmtfiIj449-Rudv7MdbASdvHtBCetJ3lo32Ioo-FbSpqU_ZzmI35gCfp3Q3oe72FVfO_hc1drQ47sCaSQrh2EUAfwQiXJQkr0hruI471uUXp2H9hdxi48HVlANke8eLYoTA_pzTg7fx6j0hzLMZMgYz6VEHZWYJTYq8jTIZuHgIxrrrxweOvmDAnXpUTGCONjvF8kDSWleaHpi8UTx2Sc8-uQH4aDa--G-v3q6_GqMUJCKXruzZ-GwsVA6HQ8W2G0RYXMA6goTPUB_VATD5z5m32h7acL4YacYcnBCZoWkq2L6W_Mr9_VT47LlsOhw9Q2XL6ZFLIg3xS1HAuMCPCJrLGJl-3B8lWnAQhSdagOf9vX5s-z_jc2pdbLQp3EGLtNkd1qiY6Ir4lLMj68oIw14Ztwluu97rk1w1je9TSeYKOrZpNh042hcACBaj42w5YPULM_YWDcGHAJiNiRo9nSghLIwFmHAwDrLo20VfWahYjGtIZqmDqSwnXoUO_RjhUjUWYbQvMQyi_TKFr0MCe_WjFr_1e9Oxx__Qhfn6JRACYPXQc6Hfy-8K-PtEJQdFCEkfAO6C9Ps80MC1KJT-iGYPCjXU-DwOra43YHOoE_RWxWsksNefiVXKCVYZrusc3spbc2Iztb_ve3u6oJflwyjMcZ2w0yyN43bsKeVcR_wOR1pHWO2x6BB0_X_TwGStEeqtbxZ3iE8Z9bMyDkmbSpdzL-zvv9Q6b4-pxjj6xQNpg6y2ALC1SX04n9haHdvNKyt5BCxw04chCLZM1CDG0enkucoJX3TuJduFZ1AZBOhe7GFx6i1-zLrRRy3JxEvg20UgFofkN2D2c6mnkVlveEflGU6tTA3DKbqi8C62hqB1C3SS0gqlh0l0pX7DzEzeZKDhaKS_mZD5zRqrLUAkNIyhPdedCZhqjGSNqCPt3hFUKZrBYGrbA4csOU0EEcCb5Lm3fUdDzDZ6ZgJFbJVA9GA_BMCwg_n2Xz3sYh3QwnqtSHuvCYdfYkuHpPvNJjlER2zdsFQp4PafUoiJHQrnQef3OTHkA8Pj90HNrVqbQ0bavRqzbtEyUjxuPNIWlJ7F7fqGXn7xFiFMfl63RhcVFZ_X2ZndBEXhjRk8LghHQUJkTGI6aBk2CRchUi5ymcP1TdK2JZj5uW_fcsftB6R_UwkBpsMtnN63KvyoiLhYcn85cXoNyPpcTN13HrjGR1B_rQwJg1uL5faNMeqLxr7lrxx76n188iSKn4N8xl5PobZQBqL_CVm1tx9K9WJpUwz7jk4SXtfkvrimD_Uu_lWyDR6Gv263xeUuZYOgM3icL9WHmXh8R3Ba6cbh5Tjs4k6SfpiPJy9dD97Am_ywrCAreN8y8xTufso36-j0GpfLEfK2egiNVlIYkY5nAfxKlri-5vUCFceDg-TeR_SwNh33-wzGoSSS4_mNb4XYkvNGGCpvBKKaQQ4-9L4I6MLCB8CnNT3AlMcecV9JxFAR4NU9h02hQ0nBxsiWWPQGA1nY5JEN7_V81d74E9wT40EiMqukcbS1cnnmuLF_X6pSgi9g9m1o8d6I69MKTFRQo0vHGEM-hDffXO2BgsUIsZY-Yxa6wdRp9szl_wPhZ8o6L8aMtyIk6db_VcuJi2zyvgdrh70VjxUTPq91rqXgAUmMOYLH7BIn2Crv-uB_0nOtm_f_govnha4RiJw6I8rL-TOrq0pM1cteM5ydKMq9Dez-EbiMoUOnoXmQAbRXFlh4H8BwXcTm-HOTPJ5qXMkD1hG4xfSfM8sP9Wc4g8Qq79oh5Wq0mkbfYI76-gB11fi2w31QHKVlqbWwdd5-w2ekijINiODk090rAi1zPgrDCySNGZ_rySa-MCsybEbT64r8l1ODryqpFTAPpGBjfcKBsCRPI0HVLSymf8IOd04RQbGsicR3d5ekOVxMuhTw6_FcMZ9y5HRNLRyNykyB4uknJorRBppLldSPl8mLmBEHzHFr-j7SPuxVDHBHZI3xKIDwRgzvvMW0PbJ6UPenPIP8lJjt1xZiW4r0l6eNWmiyEu5ccQ8TLa3_xPlJWDDxaXrVsp3CnSZvTionB3FnIPI367cjFokfoJorY1k1BcFG_qI7bNK2arjFw_WVhOIO-0WHy_D9TRzhVNF-skYR8kK37MT5u6opariwUnu2Ki18OINIKO8FTPo-IINeWcCSxuPBbrNJI_v-s2wZ6d7LNBLq7xSKotKNRaeJKGHvZ3G7YHJFu_Q94Iv6ju_zG9NzjGcyzUnqGeNMVl4-JwdZHrrF3FCpyovNryPh2joV0Sva4us6YThukQqJHCPxdLFV6xiQkLASUcL3-8pBngUTDNvuWAg7hiOXfqoXegbXkhhFn-edlAhlwvrtZ4EbgYhy6z9ZiZBnHUqkaDX4FmKudFedUNh8ZqO-D4Bq878Kron8uxyJGeXbaXSV30IH5-PsuQ9e8_021aFhZiLFh1MmJ1xt93CkolCKa5uay57rfOjCbt5KWJ1JxDLupgGI-BNH1XA0hgZREhvlasT33UP8wSznaxbWVTqL8f1UfiSZGORCnO1B0C2aio1oq_BpDpZyk_VRf9lVvpOmhV36xzNx8ymb2XiLxOTpGJXUfNtqWYlNxiN3B-BajqJEEp-_EUhXTds7R4YbNbk7YXPZFgCrJJtCjHkfYqLBdgWwwe9cqI5RSdDcM6KVnb9xEj898sj7CW4jpDnQL6uECEkEHZzuQ0pgr7zh72dEqe2cjTdGnkSVKMtTzphNvQ5pUHG-K63rbCERXKgciupHjKrrOZcrue6rEUrW9tpcD-MghSRpQsG986zfn5EfgWUwBZ4Z8KQ-D296OpnXg4vA3tzVX4fRd65h8Jg4X4xjPEAgF-RkOSwy4_9jYorF62_B2Isjx6wnxnS9t5d063K47Jidbd8OF24608pnKiYHHNqYeBqF-oYfgTjIIrT9gLSQLDhAmTlurSTJpfKDN2qVWZERh615nlvUPJhHuyz0_pMG2-pWUXPKWyFkN4K3Cwv8NduiKhS8CxM8h_YRxuPC6-1lY9Lehux5sWiOfF76LQNF_QHibWgypMJM08RR6iCZ8-FjfzzIzM2XlWmDUeL0CvhWzfrN1LAknraH0JvCE6eJLMqNMiAfE7lWZShJ67hZLBoJL4BLoB6BdOP8BqHNhlNhekRjyvEdvKvpDsVorS2awzeURK8Ni8pjpGyfpX7v7njWHRmih35JHP0HB60PrDPcxRG1_wEOPUBO5ZEDX97HuseiG-C2DLU1FsO-ZAQtShMkuTy18lP4b0Y2_YeoWzohuCG2cKdvQkJH96fjhvVJHBgn5h8CaNaBCOYdmP37k0jMHD5xvF7IqbekRwapkn7AX-etFolfZP_fE20EowYcKvP5q3iYmaeLbLqPYJcrGqT3jXm5A0H4kRHcz1eUO3tPTXTxP7ikhyenNmZj8zGWb5gUNMX9rblkSKKqar08aM2QqAoktfSlAmWnEdiR2243OJajRizAgpkTuB1Kby2OfUlQ5Jogz27OzMlusYbMvlKPqyyyuqBUEYKuBALPtERFrR7kkyCd4aMQXaBmbI1cbyIAbZgbZjvUBLD-_Sl6AQoKrd9yz2WWEleple4tTzqu2Dt-DiHYB4QxSOJoy8CAu-xnxT7cAb4WKfvXmLdvzIhQ9yDJY3LEP7ZRJij3b1BFtAUxsedmEixun7HvpqBhgmmr-gNxHHQ2wM7zlatF6rMCTLXtJUTqwyZgIJh1MLqjJS9cRWbPwrsu4PxI6AL0I1kUR20mJu5xYrGVxv7Vo2HeOB3vOWYiMoAmW6JLvj4WuBoq3kOrBmPllOr-vC66QYXAH9RFpqC2rSYz6Z924GVpGv7YNEzPPWXRlkyOqTtd3OuGsptXykZsNd415ywB2e4m_Ef0u09bG-Oz9wbX0WoPnIBceSrgkaWATWsNDTFPEJCn5yb2kBgoTeBazied1ddWWJwlFBO5Zvkg87BM7noGlH3NNzpN5CqdVwinXMnU79PQIFBgE2MJLdUKpPq0fuZvPqGU2IOuK4VNrDDffRKSQinsoRFhmu2FcaDDux6Mw6QJUozkCOu4rKAWuaBeVsXFKu0tLtiQyNg2VjE058Kl3sbvyiO27u0duZftc7zDxi2q8ip5BOBYS0IG-zrIn2SfP8UbxCs7b0FM6yxVuNH5V9LGlyTouhWA5SSOJXMwRl8YPJcpS937pSRlGUWGrWq21ryBTk95CZpzdlGnn-7Xc0rxkrrqb-wRiepb40LBe4q-w9lXK51BPAte1yO9vr36idJvm1Ajw1kCgQXL0ybiBlj4A1WcQ5C5fgVOwhcYyIOvRrzpUPqcl5UQ4YHbBbWrae0U3aHbZGFR1ctDF9zGnJoJvtSkAMPg6n_y-HHRFhSJGrXtbJs1BLPYYzKU-wZsVonOzkmMf_j1MdxF4ciZtl5YjSu3GOOwZc7fydxcjm8ymFBmrm-Dq4SWQqls5YvhnX0EudAq82LEMIdrKD3mPUAAnlPF32-6KAP_OWDn0NNVvgund15uA_mgxkPwS_-mnVk5dJONLjGh3kc08es7hHVPGmPkqpc-ln1vRYJIvTHdhJWv5QMHr-OntmE1qWXe1LmWBcXvC4SrMdFQLLkvOx-4O8Hke1tO7ujlcs1WDt1ZIRNfaPH5DUJWRziSR5cj1zAxfnFpT8-EUMRdW4mWuhiRaUCcmb8m_rdMqioQ9GFxhXbotTN02LtV5s_BAU81axOQcPCHZ6wTNpMLwv4mkIBaz_l9SctVM2113mw9iH9Wy7s6jfqsnxp1PPvKOSk6EvCHE2NIsiwO5CU2vmVkL7Y_j72Sgevoncgvv_3TUHc0ljR4EiidmTdOtrFcrUfea8wI5pLRikiZpCix4kjifFBVgz1U5ddefqB8Wn4TquhrQCUt4Dw6nXPDl0MnuLmpvsV8dEfM2JrokMlqAeY2m5GkB2NsOfa-1HD6WtUFt58Guf56ihlNxG90aFsbGk_ZDEjrphAmpWginLmZb_1M9-gnEZUBmOktImS2LSkkvvLvVI42TzT9jh-idem6-6ozT9DcAS9TW0pKnTfNs4f2csDQXA5zqkKS-584Z6Svwf1QmriF5YVRlZmHrWjgD8c-U7L-uhWwbbqmYXLRgEdN9BoFLw7LgUDOTx2h0y9bv1QI8rn7_n0Zwre9s2LahjB2alrBVmSElqKahP4kLjyZAuHernSKv6ULAZxLMhnjixmdDuAxbsYo5Cggpskwa4RI0VY_8hpIfy7e0aAI9-EMSSmC8y9CdhYvCrs0LHSP0HQYQ8gFHC-iiTU9sOgaZZeURy5G1VIFYJ9KP84SVS9pWMAlmeMVGewjqBk0_Kwp4Negh1t_3IZkdE3EwjmX7sIbcrAud4Kn8-3ZxyscR_ISc_fZlDIQl3eXSZ4Lj7M5T5tgLhu8_339bZiEP3_lhqGarPoOHfWBukSKBZyM-sWmTbFesjOhk__gvnz4AzqLm1UmkuOBX3mGOky9DGcRE57eNSwjlWf_fQUdwsIB6tcc8RN7tUtU0Ji5W25cYGyqZ_ZEJeQbOqyZ5pXVvXcbqxO5f8aB_pPqlc0_Pr1NBolYtRo3qVQpxKUDwQdV5YoOX9VZgoDPc-GCOHpkN5llD2v3i3BVXeOuvWVwPt6UYkDILswSZkQhp85kUsRnDERE6hHAlSJJyunlPjPLU1rZWdVJp2YMkR7eNhJLuDBpwKWI9GWCEyZajJKHWTRexTpqXVubGQvcbSBWAO_Wo8WcxBCXvbXCV3FlMpdrHbRpLX9-n4MQsBph4EybHdt0kJmW7K6Dpw10vgiHIRE6xrRKuVlRVDlW2uVpQoYluVN5kRbHFeJRRKJHun_BD5rmy-FR8K79FEkHBaOSkQYSPSPDhmq_vI_A514nzjdDQfEL_3RIKYpWID0HmiFFB04Fwwz4DLdElL2T4zT0PGQW7ZfZtT-mXvaglBLse0EDQz2h8aNRiJBMIkKHHCy2rBch9qoPF42fCZOdMbtZ5I68kXKVWZ-f34geDaAEEp8xhE2mPJUyLRKR2EmlPrhWsYxdChk7rgnuMbc_6L4dsySxSw8iXrBcqAe-ctXtSbnYipaHQdV8CixmjL3ET8GSteZEjA0j2RzhlKFE5AyM7LhF8RqRGpJ2taCYVXlhG8rgwjQoa73cdOJY-rFApPwxCnd7ppy_32q-3gVXxfGkKhQzWraXKB11dKvl8f_yo9vcYbV488Su2dVQuqRIn5bOK2dodE0LF62CN0seMCdBSCbbrIxY7Yib1egAkZ10Wwr-prY69bAYOhC4O0eqayoO564PlGVHeHUnL7Ret4KfmDgHJj6vaP3ZKiiS6bhThAbSC7mB8LT6N8jAPh-4-FopRsB23wHia72MRPUFZdynoEslY9t0Ygub4qe1yEFzchBM8t2RkRwNa0aZP8MkTUc-LN-V3E_gQanWM_zwj2inUUGsRc8moO9Mke0D5UVi1z9vM7gXYYi7g7dM3YAN91Ku6Q5C3hUaJnKR-Ahoyd9cAc34onSaDVmVGUWioLjVeIjttfI1C3vUSJ7XO8HKJeakZu1N5CRoB4bLHW93ITHzZTSkVuOpyEJh10X9A4We2-Pc0ueyVJAlUbkIpn_SSRTDV6BYcwzRUjlezmn5DVTE8v5SWmAYcwG8SSn_TH8jHiMKUQIyleQy_R9q43YCHUPsbrzl5EeTW_QJUZsmifiU2mAiUtLNE0YtFXCFp4wHp8O2vnuKmfWzH0kZ_I6PfFp6aqo1HeCxTHita7QmjgVlV0v51h7vj98wHMdC7j8ojET1p-Wv-gOnZy0qhtfq4PosjNCvQ3Jw1YvNM86Akmg3-R6qwi-9K70Zqig9JIzx5aZy_Hv_JJPY-ITVXfi836uN0wkjQ553ezdN2KK_KvNAKvSHmMWF-H85Ru8qfOoa9QFnmadvQ7Mnr24R5KvJej5PxhrAPMr1qgX_qaKfqeF4a39Q40hvcxw1ci3V_nZ-O_7GUEFfibadBAUkFzJdcoJgnqlxpOguyEdbs8OsiCBaLb67dqjz_Yode7R4YiGiaBP8yHW_-a5jUcgMz0SJnOlRAFoTqHL-Xs40nYrTAxPK-2HQ-bo86UdqKsI8cScW5hnYRngnxONdB7JojM9H4_ya97YZqOamtSfWqfKDHSbdWmx2zvCTWCn4BrfImIwc308vCBvVSbxSEJxJpMxPiMwe1ndRcJ2DK4tB8M1Ce-5GTK7SCQ5MFS3BpSGUIEsdHklFoA25nsg4Gf5Z1ZVFKiAuBnpCKM_MV3SuVpMmCx8FEUL6iVo5wDQje__TYmoEdBoDE-PC8WykSh8g-ykXnKMVgNoqPAv5vT1F2aJeqEUdskKVYJLPrTQnmikzzO8iqjqeKl5mnVdYphXEold_VpQUleYsTzsrwOnbizEpR0kXk6ioQmn7rwNCxdCyzKyFfIMHTK_e8LvybPp5IQJ1O3jIa3uurtDXnCinsE8fUuLUjK2fPGaR5rJuoxcRTWSLXjQaiCJOsorvUnrEuc40Ew7nyJlUBkNZKIbJ4Dgr5YLfX5kgup0O-dEMehuMQIMbWVmHGnTMIU6DH5r-aaHYbKGARx9m5IsbBOsH4My6W2JyY-9NCvHrs2VI-I349TC74exqIk77_1JXmFYI8hVKB8wnJVi-hlJkEjslkQfD1De6ogysSXBnCmmDGd9ijR-9pchT7u8Zzpx4cIImzF9jbhw5-yI4Qoy6sXKD0JOXO7GW8pI20WbsbzNHdnmMY_CgrY-GpcJ5yP9xlaQrSb1dmKtJG4Zl5rBzgw4z1242KHKL2IoCQkdhxUn0J3YHBAMCWzAx7YVLB9ptCDD5ySxsV9POoixfK6ucfvRQMIzwW6IYatsdf3w2r58lPBYuiQdPLtiY3vFXHVt3ctHGVLI1SVrhMh7Sxg8BdPNq5pdyfSBEDrKiUROYo-xlTbtNP2P8xVn6-hSVJ3xb6gJLtwNPcTivbakIJnfEkzi5SgEmyUS0KT666UgnR6PiMgV1M7faMR5dwZ6386G6XpLxkO4V72Jc12jN6hwJy4M5s6jNcbgjswtjZtas4qCyBN6RlWhl7XuaGvXZIRpaQsO0k1CJkt_GC6SOSX_fG0YraGqFis4wpxhwXVJ1A665tNVYdhZgB5xNoyvLA4k02t1Rb3Vo7wXCXmmKniXovjBszSluM9W6JUvzkTvfspMs0klSzwEnzqLWgVIIN3oaLMu00qgtb3QG-Y6duvhSG8GAyGT8RWMB5j6xVMjqy12wbGACEnW8_Jy-0HYtixvPqD1ca60zlMTX1oMYHZW4-s9y9L5D6N6lfckJcQXWHrlbfNNrLm2xXxODqRfVmI59MwcNTi6DJ-jLiNCIXlKk1aa_cOFriv2MheU-4EUwYxAqON8E-Mu2QHatD02FbQCwCyPGQOfWbGJwbExED7amgc-w1Kf-Z5892wFN_nM51-af3thoxSBbUF5vig5iepqCqK6N8OFrL14hV6rmfXhfSNfcw0o-1KD0lwqqI6krbtSvH-u8UuH1HXK9YpRM5DEUUVQjEixrX6hoesKQzcvTid9hNmv7o_-A1vg-6LK7KXYuKGyTUvigif98bKuJBovsplr6eg0UoZG-OH4b9ZKmXp6GkL7l0yZjcSNm4EvLoK2qMEWZuHfDl_ovtSkOs977OBLEqVtH1lD82239S_QHc59-s1yRkWw_8erZAbomREK6KsgOw8AUY8FAg5yciyR8cobRWwA9Dl_O1eQMZ7Fe8kylpMCYfsJvsYt9E0puN3o0sojSnomAFxSIJrwOQP3SSZQZgaqZkNPzlT-W17l-WjDyPMIU3L834U48obv12mQNl3S3MNardzZcot7cZtcq3GTWhTchzMLTFy3YnfcE5OrP5B1T7MetfnW9fbYl4xhUuL4EF_qeyZvoJO6zotrD4uegeFLMLCFGHGISb7icWVP81Nu6GEl-EiCKvUnm6SDJR7R6EwJV--NEXfxfKcqggwPNyPRvY8ak5x6tmg4ptSgeM0A5b-39Y6XbWabNizFsyFoTzYPM2bW7ulmPowhk2Fe6B-Xu5ZsO9Zs6z7BgoXjf4oUt-iAQx-Rh4Huh_thVR1PjChVMB1aiodSsPpGt-uixdE0B66SuPPy2Oxyto3-qkVSXw0eHStuKXJOEDgUqa4JLXvd1w2YR2c9AXIKfiLWIW_RY4lAWRcrtdu4P4q9PfCF50O8w_AWy0ZPFLr6SD70y76lWncQsr6KIICZ5icuAP_rK9JiLv2SfY3lMDqbWnlBLOS5TP64aZO-JBjnermElm8lKqfNQlWXJdi6EUBgFpkXG3hLVlJkCZxKoVqYRbxd3JEsuML-oyQEMTDUPKt6LL00smKBAXsnukHZmU_7SEwKXsrafEZx6brAJXbQvxqtGMW3WHlZzqgSa5Kog2_elUQuF-Zy3e1m0bXAW53fJYH72P0SEe31JmkHggSEPP-szNso9c7lpo23epC2sZtAUdQG_ORVViAwfP0Sx2IKLhoAy8cJOttPAxMbL1yLRhiZUqUe4WwniKRcZsM_rMRoqUJPkFo5fEHNpoohdtQ-fj2z7X4pxQ8hmk9g7l7Ec7316tv51qrq9wFe6k-4nLozbCh2-2iPTA85dGgrOt_SbdmOAqoKxFvNZjxMgTgYJnotqR0yE00D79VIwkgP7XEIaoaHSmmRbeUnONPbMuZzzXRBuYF0NTh9ZtEOUNKKl7tcJdg3bGWZZV04Ml44h5kPfhfrg_UXmQVnaqyz89jsZfi1DIokG4FdgHFRcmR70XNoJbtlDsPKMdtTHpjdjUYI-solxdcAUrs9vW2G0Ph9XOPJOd9e810RxT4kJIJHj_dZFmi3eW4h2LNhVxg0qLWasfOPqjYhvCcbEOiYNrtUmlkumOmsLZFUrxkYe1JZsSMt8igyy9DjHsFk2JtERiWgkx2_muu0mPHMdYQstE4g5GNffc1OY0dR92DaYHusIQ5-y-QAWhb-N1ZCsUkiiTVeEekvXgbGzSXUbe_waZT75j2GDClnS_Wd4-LFpZnH3Pm4nex3t44xzF7tSEZ_7Xp-U8MyxievjBCJNflI0VYJlDdrPGiHQv5NG9LLmV813FQCVhDy6t4Uk2m-zlWXArB_dnVVO5Ur5TidxsxP5ueAlQvXYJ2SwFtnApYn3U3AhcfLzzf5A2yxglzRoKrhZv_YX4_mN11bi2OxE-u7XpFr1hY3qh7U2pX4sveWsqFVnXkNizd4XYjbps_2gVl-fg72VY4MLxngl-XX3gnbVrxGsFdTL4zf0UF2exxW0GM4s5YE71jounO-qyk1S69fuOMUzTfOixf-88k1Xpd48KIDi9wfQ0Xy3okRaBWy3yCEl38JKfbJwjqSU4jtsuptzQbHw3yXsxVk3W8aJQ2xtRD2LbARZ5n_wa_jJhXOqwnUBUZ117Yj_sNNwlUwMRbar3fEhAcuGQIOQntT-P33irB9cY5nTs1zpDwzW0Sgd_XqNf0mmFjUkHERFEWy8C10JeiWr8amD3x0IdU7zMQk_vrMqMfAQK0AQX2Jb46Usmbp3dUYPolbv9gRn8zBphaidiaYDUpcEbepUSegobUc8HOlTSgkI33bIwVs9-3rV9Vt_wBLM2gWdfrpkwA9RqRVTK9vRMZWKT5ohoAJ6QpC2T3E2iA2fDLteeO8ttASUQQp_V9E_TpcU_YMKKIiVVEz2utH-NWaqHOV6WUTOXkR_nna1pdFHbeoBfULTanMCdQ_roQtyc1AYDpmGIRQVAyOwO_pAapq5ca9ANgFMKOulf8NV8vUxvreQDVa--tul9oh6_Z8hkWlMLd4kGljeDvx4kpdZ_Eg7zLunT1Khst3C3GsB0pUxzv07iJq4kBiLO_G0RC6tk-nys__Ajje62kLopdJCsEwTe5PlJ4OFrMKg9xGjObBYWOm1-2P2IjPxUeOBZJ1UrIKsDEMLKu1DHIQy12F1WiFiecIk-rnlS3isUCEk0rsF7G43m2yLRfhx3nRSu_ztqEjesZZa9bj1i77r5tlgfk9yQyPVzJgFChtHrbj6KFcCLUtwWjfGozd4ucx-4LePWbIWc_r4hG5kKfFeBTZLWricsXZYfPOQVejtJeDTbJNxqCCqaen5zzmXq7Yt0gkf1iM7gpwPcrshzGgGWGNbhWhRNWnMcaSse2WSXUox-g0DsWfBHVHpSr1Rsn4kh1Z2b-dicVvzvMfbF-swCJOIT8a-cRs5vq47umTlR73_IDDDnLQFUds40zmw2FEMOd-RmKyldwgNYGklUPP7y91DL_SzPq5EX0hbIAu8rZCDEmtHby83i5L-is4QscxUO1zSWxBeM8wlGZ4wXn-_i_lAmvvSteepzRgcULyH8AneJYX2vUM7ivKm5HgH-0rPti3imfUmlfSfgC7thXX4D0dOW4WxTNtClChTEVAklKWok1ZnDnVd3RJLPiZ3FhBlIqBEuVRNiMf9dKmFGg8MPqAMVbv_BznsrVfmwFUwjmBBgAnnKTnHXTsfHzW2AUzRyREt-ZIR6xNcQbhIUwwf9QAgcTg4OWycLFMiERvt-gqf5soMHPN56R0PMnt-J8bNilE69xGSFg42HNmoTlEl5SHkNHa88_NFbwIxdS4ALZZXqy4GOjnpS9VUN52PcIHPxApKnn1pc_K0u5C3ADmlT1N5drTvKUpvEPj1Ifbq2usmQAJ0L6YiAtTtcersoWySYkywJ1Zz0eDsHtKwme1pxdgJe8VLI9LUR2Ih-aREugXUboYCd0pF21iYb-WHDjmZ-4FyOeESybTIztPz0w1FF8TwHnhMRHUvODW8jqMfJlVk6MzIs32IDsiW4ef9Aafz3az-g3nxDNYPNh6zNUjnLo3C3edncLSV9ujJ5Jek1mqPtEUO9IYjm1y3gRzX_FPISt5MDzJ0L4_KWXhT2mDRtKite4LrFOaVyoKZxFX_rTm7xToGqJ1iipHyePbIT4DPWxn3TV9x19bdFLAsqGFy1XvQScfxNMT0aXDOJHY3VQCo4fDJbVJs47oGc2H3tZF4d6DSXytg3Au0Zp7GnqKMsUzu79ittkefjoceitc6MEIQ34_4F33RaZCE03ChRjXdgP20qFYNaLiIx6X-pDC6d_VSwwXj5eiUVuD_qtrf62wXtPvbilbmJyLJc1tl3_uCkwo9ivZny5Kb5QdCRZdBZlwDSViNOQgS2EflbrA6MhVB5lmcK_7HoVmnqOVQHawmOo-bBO568ZeAJGBVAh9EwK1aiBPprIM8bUHwpUURxeALFH88vsDSKTrIGdIlU89jRyJjApRsIjM4JTKN3dRsUcpCz2R8QCv2fMmo23j0ScRXVUvHpdgseXzAZTOysKAk0RxK73sF0tR3vn3sKCj-hqvZNe9E536L9QZESPfXIc_IU9EiZ2SZLth6aTHNmUVhIdNvSeDVdFmn5LNFLUG645_3NSNQnN0ICq1c7P5n_Y7N6t84HK0bwwlqpPr8x-m8FXIOYPa8A9G6RSNrVy7qU4AGCT2EhIh8C9sIsdkFbqhf4AWxOMFzFLeq9v37Nvfa45aezOy_unopLCMj3i0r4A-XH8D1zs-mG8gGH-3DDdLIKpVSXGSaS7S2npSkkOBRv1Wf5PthRZGm7XI5eRnh_wOzPJnZII1NVlnGZ64iA4uiiPK4GjQ1PCqnoVZWxYfs89qiVl0w2BKI14qaeE_m6ytla9UbFqni-8WjjGgmdN7ieaV-z2TIdyBs1TciPFF9h9GllKrb0wpgzY21zSys1qJLj5BbFbt3sBQhN50mh8nPzhaUlmKvUSrh8jBdmaDmHNfqLcQrneAtwkzOKU1e--qDWr1nVj9x9_Dlv-r7fqEFs_aW8gFbluRnxgiOGvcLoBEumEXJiYCYb5w-ysUPikPMYviL27tvFgykN6CbXytPGPGyipreku76o4-3Qsd9ClTNFFGwAl90WlZp6t461mseLgLnMZWYIL7Qp3Yn45Ki1O8yDB6bs_ZZOGNGZaN2JzT9yC7ZG24U91YYbQVOJ9xcgbmH6XkvweoSLtMw6fpwIn35UkVZJPd4jBM6k19oSgUBzPaHtWvQ1GcVsomPfPPWiyG1U2DqXEtmhi1CE-GBuhKYGWQ1IFvI1a8ZN10yho4CLF5wxLNbLOyEUBoKYsVnmqV130NVpeCK_7hhDakGjaUYqNQ0KunJeqEKq3hMHW-8Qh-s4rLqYsPOAZwUr2AkfooW6cApNHF1SzESsRocyGulxhNM9fVMearC-56e27knwom7Gw9EXvW83SSUUuhWdBVg5OMkJ1DbC-rbVtYzCuAv-yfNCi8LxSffT-EkDIumlqjFLvdkLlbm9DFTEU6GNPsvKnOcgabSyEyTwB-pu9X-y0hfL4YYaZfhYdO5i3TAuvL4qx6vmyT43v9WPk5kN29vWx6fmFNKqKiHaSRCO3ao425dDVXgkFG3vP0aCNFhwFpIlWTD6V2rl9OILkQ8AXYzHfAgFY0ulttOVdx7JqvLCUG8dgyHNGnRdN_SusERMVUzqlMWTKKaMBPZimLwCxo2NTzycjv0BQ6V8Ukr5bwLnPw5BSQ8nTitNZeC2boghFYDjFokvm9YK0iQapYfKmpLec2iluB1J9CTCr1A2yicbluA6Ts3q6d57T1hb0z66l265749ynmKXqpWE0uxgGnCAgOEO1ku748yAgy7Nt1dKnwLpMBDiUlnXZWyToeLce-sxhGjxfFYw1KqzyeMYETyNKT3CYGpffxqrmngV0QhSbWcrvICY0wJ1gP_gkn243Rg3TuGOp4roryJ34-vd8tXuj6ncCxnw0TA_CV73UXKPVzAotTwVVgpgy_y7RNCfLgrOSUc8vONgoXNBq1trZ-X9HKDHawgAT9KWGRdw8iEsFfUHTnDjeJQup8wh46pw8Hb6HgFJMYeZGO23XJ5bADi3YY0QPZFl5Ztv-2QAzN_zFptZkQBZvwa4wVKszl_6YMcOnCrwjJI9jGe_IUsHm_jklWGWt4aRsbdIXP75PS53mT_mCzHcI7oiz1efd5ZvyPCRXlRiSCT4yUaOSsQ3fkfrbCsKl1G8G5i__RYApy3I3W-XEJzivbetFaG4g3lsQjHN8Gy2WbcnOyLorqxWsNWRt_2aKeCWnHZCnFBtqQ9Ik9TKNaC3fPPiXs9a0U8RYgQqld-u738WVUpb8zEgMavUnjkvDTosPd3AsM1BvIYx0wLKp0q0LVrXCeosXS8Ob_eFADlb2Qf13C6n2BzBX-I1UbWlZjfXXynat6Apqwm7-qcqEXxZ4_-3ABZH7bTFnivN5X8eN59Kr0Afzsu82BSTfo0BZFqwJtkt2rfDXtVToLqcZsLw9cM_2jn-Dr0plfenzt_SY5OFWf1PmPEy9UI14wL6MPE572Rau_H2PQHI-Ly96eehLyLRyXaT-p-cXjSSCHN1Pq_G8HsARuv8ZY3Tp9RiR_uZsY7BwBm3St5eCfzYP0fcDEUSkF2rJpRJcHVIb28Uo-o33S5kd-dM7i5-ayUB02_Gp8bvI2TwRxB33NGlfDzp06OQwQ7LKoLowo9x4Om7UicWediIPdf9xQPOsIBFTI2TnI2XgtjGZa16zdWwmF9FIX0HqiD6U-xEu26mncTerf83BSAZ2JGMu28EuYbUgeheuHs6xZ_SOzoQHsrMEUQaVC12q6dppqoRzcjib7UTwUtuUIao6ChzycdRkZB406pLbUbu5Oh3zcsEjeV12u6CnU-bL4wK9bvtHmAK5bgrl2zTSZKRb_wWWh5pP_1Q2QZm6Tl8mPf2iwSp_voxev-lWIxYhiMAPcwtP85wZYxhWwCs3PICfNvPUmoJxkDJL_XOe7iyYP26M2oiq4FTh2R2fwF8q2f4tQ-vIO6z28iau65yH8_WOUOpdESGKEvf9rkTLzU2VjCGGAdzusMSTQDarkIiQV54rbjJIMEgoL3GCUV8s8rI72uA9vKDt-xdI4wJ6mRdj_pODffO4nXSCS-31CwgAI-0hqLChQhj4sAtlO5Y--pzTXoMIE760tf5RVPZkYWdqf22YfZARTGL1Bf0h8c_OmRCFJcP0yJYe4INUTgwlRUjk7ISqqd5cIY1Q-HtKHIz-cr2F6bZrA5FqlI3vF6xB2oMnoBE8-HafdaL3N334yDyPWuKRYjCkzpp4oiEHbzSI1xnYvgjPImxjRvJbdffnvItZUSEPbJ1GuVhh5xLOF6Q6jOSIPkhpqtALGX0JrD9TCLuZTkckiuit0mA-mD1HEn75-sowmFeZHfOCugl30_LRgTSdDbjpx7y6IiKX4pIwxh-znt51nFK6rZkX0PLxEGCjs_4gCQXKsaL9BgDVCeeDfKQ_TUk8jxfkls0V7gJmlojOEl5guNIlDHMOg6jHtd-taVzjqvodgRW6XpI6W4l8dJVXf0-22lLuxyfxqI7KevK_-_HtV5LdEOaXJD_9ulCdNYhVwZg2V5HMZim3-_G2qBkqAsD-FbXQoSD1lrLLPzlS0vKAUUGkaE_KlaLSG36lSpa0CGmZtjq_uBTGbuLQohaX1O-cho6kRqMG-pwhCCQ6eTD_zJ1EjjOLmYr0F4YmDI5sgrHKmvabgC1NkWeKDaq2yXB8Fmx9RwysGmzQMiQQOhxrcy4k9BFfIwABcGEZAU87RK-aeNNh11h049sTnL8VHce_cMTWeZ-Zt0LJVAA0l2FO4jBM65izgqVT6kn7d-3xzTt2m6WksPRwnVsGKRzBXa0ldsZPZv2xpqxscBeTKK2We01gq6a_W-nBM1ab9wphnjUTfxogYGyiG-2AtolsUpXy_a29UPcbrND7Ndb6sy4ehnY1Jm_YEWUq2DIwmG85yCXzyd8EGF2A4UNbo_WK5-JaWEPLWHz8JsVfj4oQU46vf-b2vN57snh81orAOhB6vhdNxs81MwjHDySucf8e7bg8I5rxWAr_Btuk3l50HM2_6hAkrAyHA_Se3YrKsM5UqGkIXFy68PsaT0G4t90Z16X_jgdShzXspVRfgoAqvg3vc2R6gLAuN1YKwilGFCAVn1MPHOlN9xbfvpGFjf-XA6AF7nf6Tc96FwlWIF4DAk4E4jMyJkTUHKZFXOdB91LIaWkkqF3QpN4wkpI_4Wdae26MEFZNQ6hsNAyQMBlkdmKj_7bKK4WIgjBOzby-2_nTtH6N4WOnr9Hf8TWdmLulUc96cN64g39RtSgxBfHI1SCBbm25CoUK6UDpMQOv6RkCANfPenbb3gqIsKkt-m3ewUFp8ep02Kd6T_tfIvM4hVGgYjBcDXK1g5pvEFt3tmjhwiygGRFLrciXnwZv5-8Vz67INSmW5vxoyhqicDIEZBl8NOn7zRhtCkY81EAZvskfOFfXR0S8B3X0xeosVdG90IjT0J3JOfHoTuHvGi04JB1rLeoOS_eWji3TOby_CI72Db2lrBd5gdECB--g7jNdhVweiQD-rteaEpySdhZ744ROCfPRjQ8NR0dM_dN4GrFsCI_1rPY8sOEcGNjklWRJ3RfP2729g6_hTVHbOMN52Cxru5hFKHAmkGA3i4pEp6o19M___IL2j9BirmPkVXtrJHqmp-Q5j4hOjrd3pDmxSd9MVfe16G1lkSfi8eh_2OI78E3IFgfpQYyXC2sRwTfkPBOt-dAaMpMsxzkN1RRyFU8CLG9_cdRRe9Qsg6py6Zv7oqWpJf5i7GriQEpMqzAN1mdKc2QXpx_n4p5OWtzw_4qmpJ7ThWI2O6-NppSilppQznFEeNKCJQqBdApld_xdwZ7OuxtcLzoTaxQrr6YY4qnMj2uaPQCvX7uN3zDiVCK-xzOOQ_rrIuZg_FiDIQvSDnP6skt1DRsmowYelTyPzxyt0rmQcU7_pQJknHg58tgnGASYS6ueytmk5A0va6QIVTWDnj8zSwcufcDLaZVDi42i_9ZNTdGk0qNjHsoBXf2-w4UbNn8Qt1oxKeDWEq4IUFXRo59SlJuSZ6Za0A7JcseHakTSbsHYyuszsKOyMKMvKNvTbRzx8G6wyW8dqQO6QSnMJip-0jwBYPUCUA8AjS_ucqW582TxyU90DrKX1SAneTbA4yHA8zvj-7ay4ISgLrinTMqbOEN902b8sIEReWn_hw7L8QnItfaXvC-b4ajo2CvfA7bw-I2WEEhNN5pXVWzIDcY7a3turj3UER-MuDWg0WbrDHeh3k3eoBWoBfWaoadiqrxfWDR-bDLq1WlzbJD7pRsa0oRHyuW1dXO7vUu8Ssr-IKyL5IVGKoYwde9HR7mpaSSVfyZohARZqfio-DyA_YHT-hdbk5qPnhPpFgGJzse_AzaJQGF0NYS1d69FCqAvEg9msubzQYahrNdpwLk-HTR7xtGS4OLc_DPmlk5lJtKjmcsyXrBvPW_6JnpUQwMaX2Z1Ryc48whK1s2jCsdU6b7GV4Q-wpvaLVhwdHdR_GPnEML09t2833JjYmg9bDaJjFD95MV1czyDcJxBfMvqhG3-3YO40t3FhvWsdZDViE9DhVnYVYRAmxsSHiVzYc7IcFbiwTs0KwaBgiBC0cuZmBnJLHGEFz2gcDFdKHHtpwEVT8efmeCl3ev4d0RQxPwq13H5EdXvdNA1lnnCRkOplAPHevIny7MJW7R149QwueyzgB8hnUiNpULIAF7i55B4jj2h1S3pLffDWajUyrdebnrv7hFvjgZJc028REjsKkU_2G2B4otoxWTPR6zme9OBEM7iFcqna4JFRnxuaYbsfGBWemn5XDAJC0hs9iAJbIb_5YWLE0VUE-syQsVtK7WWHi_zXWUMWUEc7v9McXP5y37OipwL6iGWlAzc2RdGbQO3ZAC2NAH-kJhTPI1uGV2fd5QgxKLEl30LfzNFUHEUy4IODgEP8B0o2k57RwceT0d7w_zuNCTR3mCDTF54bG0iwAkYPHgTPCuxWDOh-8rbr89_xnE2KveiW0kixSRxyoSy-YxhFMcfTYq0Z2Rndgc6lKVdVp5PSx4LkZDcUw2Cn_BnDn8hb1CFN1cznUNHu2D3h66pevhxkZIMChaSR_nL0GaODbz_rDJuJvYp8l2foNIi6F3leUJO1uei0kkJXiUo2_PGoAsvSBxD0oFAhZDmXj6TrF-C_KzbcnYiJp62LRNGT3knJJqEyOudGVSjMlcasfOSQXLYfE6WjNiqYpw8246YuW_okILwdOJj-KMgoG60O9lUUi_sL7I-i7GAaR1KQcF9wOG9FDg3P3MbsGFzLTTrOrYUJMMbaCoRXSP6sKmMgobCjj0_4WbysNbYRvvu0K9d7pHZdJLqRLctosqEIhSoDFhPxmngL-_ZqQztHWH5rFhGi3952LceCDIAIIt7Z_23VmjyinVlLvTZ0AuugE_d_boYSdCO6F-PR1Prvuc0F76bXh5m458uZ6onLQaVn4VJksVwrHxSPbQIVAVDKqaWZhcM7d-dUpKoh5Kvy1f7Dex1cbOt5qRG0g4E5p0sY4FVb9YgTI8nH0rjhVq6gAtM7ZzcDcEaw0rPpiuRjL0-fS_ncK5zljxFqHc9n4wr9F-0WzqopTQATVeebJnTtC42BFZB3QgP6TDYgj1JGq1T2AgIxHK8OAAK2b_bmfVBx9vkn-gGMiyEz8sID-zJHOMs0L9Rlw1ALDjRxYn1i4QOmHH-eUWm8nKU7R1xcFTjvJlLVmrJ_Tw52830g80v4RzL1P5AazMD231UCe5so8L7WxNk7FQtjE3lAwRJbXJzusH-7Shn3I42YK32-EfkIFlLmMfJCPzktphLnROFnr6X3WBC1k6-JBefvs-9_-EAvF540Qw5bOt1mQbjaosxHxvZg_OevhFjQG4sKsrQEEVqGgZVNGKPawmNFLtALIpJQSmIVA7WI2VRo8EXKTUHb--JzSSOqkhoWYJHmbi4wKtYl6mCuIRppemV_l6HNjE8Eu-HTnCqF0244cTz7EZ6UcECr9WHM3WkgEa9HRDnmAvJb2ThDReEZ1PlsjM6QEFL7QVkvydOgirvNz_iewM0i8xL9bgaT3ektFjlNpXa-XchpcbdEPQGWyhxpN0_Yat9mVxyBg-0S18HTYBeNn0dqnqAz9bbair_VVpEHF8joLeFna-V6Ig_6crVsEdSgRxNylnGN1o7libwKhA5lFAkRXeA-nvxygrLsZQ7pWfeO7VRQupjuih56j8BcvQzktdnA5Ugl_s2i-Z-Uzd4msnp6K9C-Gy8n2-o53CJXOB_r8zhZfvLF6f4-1iy6Wo9i8RfufUB9WbTedqtY4Vf6SqJG9LemfltKs32FD5yvnhxNNsgoNyVaoYRCCzy9ywch74NOLJvAlHZiJoRiB9xB2hSsFG2qywCShZCRLWGe97NTPnfMqd1KfX3rCCvwchCCG3xYlQ2jZmjHRaddtS0_aqlzDa47KC2hlS3Mv6dwKr9cOgrz2G47t4FN6dm-hbH1jrbDgzXQTJS5yv2yMGFYh_a-KfigwjDDAjfIwb6sERozJFQEDjz5WNUqztn5-rAp8zfd6OdHH81x9jBGiWo6A-wm-SAYWDnm67HHM1wF2EMxoT6rQejZ9u_stpm4yD-OiPLy38VOyzq47xwBlpeitQYalY-ybD-SvcH8oK67QeDM2AHCt85Ng1rfo4qaMzB44ZS2ZLnfrBUNzgcmS6J4NdgmkAxuBSLAzT-UpQS-3rOoA2I-S3GeohgmUScLKQX4ppuURHpseZUhjMn0_4J7i2QjRZTO2EkSs0UB4caS2DPjkcb_bks1hndPhQOpBv2GkVGkxA6XRP1hnS-MZJYO2N0DgCDFLvaXbfal-dYvlYDmbQGfwQ_aqRmTTGizxFViDkKqFR9V8u6ndgR9JIVY_5EMoEbLdld--dnuNsr0PNl7-sHeCAN0UNU94xTO0fwrTqKKjIRNqplo5xDHKOIuDOb_fQQsU5WIEP65kyw9Ykb8dZf9kzygFaNr1NRkYWIV2oyGIArH8dnuDHa3CtKmMIoJ0MNF-RnBK-Kjmr58ce6ZjhU1aDdzBT9sCM-1X1mojVt7QKilx-LhOBM20floS__gtRLeYFaI_DNlU0hyVRGi2OIfT1UH__vO8ho5rikgFR7nqSDK0ZUg3bXOb7ozUeiTp3ZN03Bg0BkQRUL8KmGnfn8e62zlXOUqqy1Yv8Kg1zbExfxMSadg4UGqr3KE_IGLxfsbfi6igIa9tlOgFDik6rP7-_xxgf_nPxIhui7NA823HCNYoAgmLvo0Dk-pA-LttlTUqJCJj6u3BY23C5UQV2WLyA3jd_d5U1Yc__e1pXYhOILg9AW2wr9eKvV7zGFXCuMD-JROe8-uD86StrZgRVHijBRf4h27oa8eXSjBA7nSBKuY07zcmEGAQVeKFDSf3LbYWYHkRdhV2g5t1ze8H9ibZ753-CzADG_H_HEQ3FTs70tvtS4OfATtTdon2hT65zeDRZxRBUhmImTG9cb_YQmB91U8_Rh9tDkEUtfLCayr_iYlHvu-SBMKrSGo-gVenkptzipM-GRBqNuwrN02CVT34ePHlVoZrDSkrlmXbyVg1U92n05B2zl_KH-jRk05CqBrBBoisFDy9Q1_oojA_KBDIUTBprnvIP1Rb83htalp67pHUs_OTF-RnYylRaucRpQF2Krro6gPPUA-473Q_FZXnCa7E_cwHQBSIW7adQYmX8hKu-DhsDrIXfCGLdXAmHD7Lp_PW-kZaxA-yLu0EK-nOzFHVC_QNK8CE7CuEj_zV8X9mCBhGcVbxaPftdDs13XkAgYZgKDRTsTD6aTglqx3Zkd2d98TY3VtbZwTGTZBdoR6SE1BPvsg3I6NPHwGtfUzy30K1Rxu6EBcjY6007giKdsjJ7wtfbvOsJdCKGYWyVxe8ikDkz4W4uZXWYY8Jcn1Ig6ld2larxVUKG8Ua1pHsFK30xazBlqjv_5H1a3rCm5UinerSNHHNml_5dip13wHWJXVNUUGkWfE21ToKAt1aJQiaKxEGtwiVfoXdo3JuLLNaWZGGSip29RmZmgwNLT3q31aMPg4ulZ81z1YS6eh9u8vREOCYZWFcuqDt3W_zr_rYJr_J69utCXHTL_KWvpJZOTQgPat6_r5222x5k4LME7GX-WJh6JSDRK6HWqomGmjl6iDYQMqRDfL_1ydU7yL0qfD5YGMrD3WDtlMtPio1AMuh3Kc-Pxc4SsBs_DeIBXLhvmgicMKlJ9VhA09kC20jd90RI4TKNc3_Th8WGJe7aUFmkf2XN5ozO04ucEg9vB9edouaMrfkV75feV9zSkLtUdt_U7QJmsO5NBrTaKNHAWBM3cE3m7CpsAg6xa6vpa26lH7YDNRx_l93NlFKJ8Fn9vm-eJJq9MyPuczW288xXQc0wCFbSpyBKD3sAP_WJP-1sKWKJSmo7stqrpb2kLTrzic-z1_3v2T1m5jGNTYeGhxAzRqj8Q-GdiDsq--_wqZuUmhn2yxXM6RPgIleE_icXEhxoF8SDnKsbmDusu4qHxJGt_DIhnmwvGTVz1ND4cwiPsAHErBzOBEHIVDa_yZPF3PVVLyUzyU0KaWLmNj91ZD_2ufIevDqMmqgjF8pWspVnshvaFW3YxZqe6sl7AuMcJXyQ3qEr-GPuRAmtuxLDS1Yo47lVGBS2HHDWPY-KGFfujeHjgDRqCoMXjEr27AUbbpCGOxUkF_6su6PlhK4vJvEV9WYaVSmJlse2FHCajotuKlJPNa0UdYpLFP7ZkoXtcZM10QteDKXfbZEq_A-Z9zetNFwEA2SLz2lYPnD_JWAuKbX4nrSBEHTKiwLMmnpeAYCmlHfCwanM0NCFhrxlSCQ2EiXjEA-WUbdZvpaQ78Vy3qSjtUx4Yv3keZy6Axs9oZpaxUadlnvvkuaDJSucCkXHTKIBlRfObG55gmGOhzU-Gx_OK2ldd80CgHbSpwD5RiAwtvidIyjf4CoiseFRywVE9dqlgrMaJivNYumSOURMeqijbU4tHqTsrU37PL21dI5LlUl9PS8N-kWfVkv5ct3tKBQy-_n0KYIEuNIgQSBhi6g39YqjQGG759htPHyxCtjZnmA3DtBs199Yt1cmlUwrGkVlVZgyUYCoyU92XUhf9MPohtCYwIrl0w-gfOWIZuxyN7syDEOmukoczIdTVIDQhpWDkUSxoqHheL4SO_uGEFc61EibwEnudSiT-aVy2YyCdJoNGMgYh8AO-JkCIJ6f8FoiEPHApp6w7DdzmUfCJxZFHX0DNZ3eqP5ykemvjNsFd5sEZbQq3vt1kE3fw1_FThqc5F0aMwMTzG-oXCyz8b0RW7VF148_qUqBr-FmrCQRQikJkfkYDuEhajsKBKzByZ0D8G1CvbscBNE--ZxeugKWzxIAnkZP5_5RCo0HxaKpo_hmGjIfgWoYB7IV8drTgANRJ5TJ0EpBfOdsnsk4H99jkrQrG6XQeMg2pnFODfOBkvRPWzQy-eRI9QvEV0anMCS0zUS9CB9j_fFEA7_ZFcrXrYaTJZ8vHsyYiyI4-0Cy0R6Sa3aGU-LLsSfSFy8h5OQlUFg8e1E8uygh6uEZiryQuqHkpttfJja9mszysQZsf-ikYzoiY3fSX8EB70e6NkMV9bIe6JIHaGw2r-WD9H3MD8Hcogm_UUpHFrm1R7hqDlK157GPwwun-ikTmtBuNLmJaolKmaobk6PDBiRCZc5_IRAFhAsmdqSRcFYEp8XH4VpbjL7R20erSigFFNG3vnlNjtF9rBfbMx93m15fzDpaVJORoLae1DqRFqI3em3jQYcePZLmgpeo6TMBdO93-L2Hly9EL2CXKAsO2XysGMjMl7Umo-OHykO464N1lxka0JvnmwEyx5b_exBiOMVPGUqqUbGYRsDo9fL4PBv5YTCVSCmZPGQ16n-P_XrZBEnPQIo_R7_0QgahWQFXBM2ZsBHQPH3b6s_BD9z1E8e37csWfbjqM4DD39Z-78PV6BA0NY1kdsGlepfs0K5ygucd-mIjpaVJfM_fuoxPa6vyTn2bTiJfJeKSbXMySJuulFPYQj-k3HBKKH8pYR3OuGqD3kjQl0KNmK1u1yCmRcPOLsXjVxZsPbtp8BQ4gbLCyaTa-4I7ptFQa-2w8HfPc3uzO0NqmzwZObEZFmPKhWnu4A1p2V34d082DmzGofSG0QP4FxU7Gzlx0XkKnslnLh1hwcAkLUkZnWdTbKOHUHBvUp8mLWQlIDUQo0uqwlTuOkPKqMyLGrTKCSLh2GPmfX0RMfbN9OzmooSI4iXlSYFjvfQB_V0kWQQk_c2yortTqw3FI-HQ-QDk-dSLIDY80SM4DqJrakRHqb7evmCGx6w9m2ZvSN2jMgFyUBimXrkwmhANpT5nXCNaud_0AZd5bGCK4P8nJgCah-JlKhq6-_kGcgo9uutPLA47UV2U59TmCXz38phum_-GAi2PIzb_Las_OPUjsRPxdnqASqp3fEC9KI1nctrZrgXeLXS7p67hntGns9LPNbfNpUOJM2OAi-dnN2stHzi11ancljbEh8st6VgFLqCDI77LjAko6gZjIVamZJs5drec8A8ZyDL74dV0qY52rVdpd7CxmCwHk9qznXwrcdfK_E6kQZsfPZolFMY386vnYwm02e0Kls4aAaz-jCMbm7BKvubA5PwFtiZqQGqDEJqnvVryhuriDa705hoxMoS-FJwY6GXhpDDIZmXq7iVr79kT7F11gz7K4rAcZM2pDMcTGRZah7OuNcWtQRDtCBRa8FOF1fo6iobGchTzxCewJ31MdpLO5KKj0IiAEyhmk9GuHcOJJ3qRqgUsa3SK12A6Vxiv-eg5S4QoivJnVmwsZ0Z6yryouAVa5wWIZsQHJFoT3XrQiz3I7_ze9_37AprggRuvo4-bJRfeq6EsyP3mRAmP7-hbv9uVv5CSK9jWpwKsnt6EH4mFirSm4-7VMC3LHBRmkMrkPJkca_9kTMlszT1pTFxyL9CarPfyzr_qVoqjQZAsB_B4llduM4yB11YA2YkHAIxCF9XVAu9MVbfndML49wYC4kaBVP7YIj-Wr_fcI-uvjxZuGHgbwEJcKDvjn6wCRveIzh2EjAvjWswhgcWJRs6DMXbdvHbsJw4i_xMIqw4PKfSLFt0Mc1Q0zv26DmXY0-lTfw-p_S7GYawdExzDGiAQ_1hTXZ2KXC7KQbeOKb0ilGnHTF6PAw1OIowr_mkvBWqIc6LvDYA3rUHFqGEQYbxvMUZNjP5zgeoZ6XFabW4oZWMu3GKbqvguq3KBZwMlJT23vRDdPeyx4FMKpzzDhBYgr1VguGlwXuIFoFTbFde1dCm9rvSzozwwpSjelx72sYNVSeVG7308GXhIvfNbwln2Jc4ayfGXyJxeATf_I-0E1N5j3DvvuseG4CWbWtlR2NTw2ZhDvRsNYLr6nqcGhmZGgvGmD79JIkw7lBxah6_cZlaNemDpJUF0v8SUpQDgkA7niEKsYYaiB3vJlV5LwqhcFktD7W35_vbYaF-Ik4YPLPj5pc0FwCdx9fQbmwJzRsk65X7Js4kgKQmo7T5l8NXxDrpX5wRntyrG0XQFgtJpT7Ml2ck1qtoE6FKUqPrf9lVAiUJR8K-loxZSl6g12YL6herkQySYiV63Ti9WKSjW1nvD5g3Mh-aZWlvLboobdV3TZ3OY62eA610fW-G3n2QqhAv5dXyGO97ffDsJNoWuEsnFEU_ug-7lIJlZ48baqvKZesM0-coIJjGF-R0nGypGId-aPLjwHoDirN-Gcznw14nXl9w5_K8iLxxEwZMkdcEdv6UrGDQKpV4pwfZSgisRiGxB6SSheGces3X4omZZ0OH5kAHII0w-uSvhyozooc2LmJyht2rKCjlO1rFPnlrw2j93gyAaLUrJrKmuhFeH_gJrVyq6MlgzDXkInYYYTcjlaZq-Mlt8h64mU_Z0APpzU3F7R_MfYZ_zqDVo9GSyEYSYbbYAWOc_6eIrxD3hCRBN1fA1ym3MBf2jhXZsbdwIfj5tOND-3mXJvD5nS9etk3zKJXpL59ZqECr0iP8Nh027tQosxtCU8XYC4o-VxUO5DGZKgX2jkm_ziA_i4l09E_Y4rbgdxBpr39GkJ6b3mXn34WeUp8ueUQz3A8GA6oMDSzmMBVfnd-bk-Q5bCdPyC3eeEl0EICqZZ1bpkIGHThAUtCtt4M1iJqGlaOADsjN2I2W4vZO-bk7R5bDVe4eJ4XFbPohkCSv38tfqyvBSe1bXJpbU2G81Gb-2Br9ed9O1XHriaZUU1HKRXFZV-T0NUREWdBbu50lLbGgEb91-YUceZt7s-b5YUzSgqh-rKSJevLN8wpW6N_BwH81z3JFPC2KEqoeLMR7BQgxYlGHyYkjLxLVnH6rE_JdqHGz-oRaMGpa6Ql86y836sZLqrgTb9NG7j7FDDT8ifQGRCtlDrPjFXts-ua9Eutfx55pnrPf_yvQwfaP4UOhV9MscgL6r7ay-g5vJaAANtiVd4tdeWe0G2x880WMxSlE3nYeqfzM5_qj6usyDmeMqAP3foMHSM8znWUO6rhr4GmU2cAT8mVBtjZRf5vsF0inBpsuTlr9lO5yOWKnEgJAKFCcCg1DTairQbW_nzCBdsMdRyMVohz1AbmQUNmMm-VLR2eIYXBsU0hXSyKQALnc7i6lcpcMDdrCQ9WznwLKiSUd-VHki4NBvb5sWabZ7EnKYrJ52BE5_jE-L5h52KLcQEa6U6JlxOsWyNADQRyiMOKqMaZVWy-nZORGO5UBhGA2lbswQGf7UEm6msgiu59XQ1mlARURjS5uqi0FwESUozc4eRP6C3Xm4OZfvaAKLzJDMA-zRCSN_ECW-sF6mFNDCNft8VUIZN5ZbVTFzfM5ditr7k0S7c2cToLiY6x2G_DjDJ-t0Cd3pL66P56nM1L-4PQ-D1v7W41G6bmIjO_w2SXfgVv_5lKLrdSr6GCtk7hEboPjHtU0pZ0cm-37orI2C0JpTaCUauP0BSmE5bgZNByBLwJDmLOZNduiwQrRUto3h8ZIGCm0H7wZ_5-SfNjHlkuTQv0reqzZECSvI0kwfMtf58YV5rlNmj47AdU_GML4_bjIXQ2Z6XXUkw6q7XRJzKZwf4WeUlSbqF5F-2jLVQz9NJ5yaMpCHzMUJ0gRi_fs4yxWCDT0ddasLyNgoTD9Ck3q3SlxgMtWM7jJVbEuH1wmKvNA3yP0xh6L2bk87lSojEfuvDuZL77YhIILMDQt0bpTZUdXhQbwM5vDIZ5JUEjREHjMfVaCyHLOcktRm7CWCqShXuQBBuY_OS1vy7a0KOB8RyMv-JK5zCnESYzC6fssYaVUAiIwQL8Idgvv3JwNOgutUVMYlLpEaM22NCmIY3jJZkbvjCcxkBe3UJT6i_4h3QPlsBtQ9x9MFg_McfpoQhlZTU6mszE0UaCr3xhPJexkdZBPg7khtdH-11mVy1pTYCPqDF377CxJe7gAwnpZtrpt7A0pADvvOpAXMYiZcgmk76-LL86rulP_4aQ7y3Vq5xpam0P8dMMuvPsvGMBK01TM6dhHJSYgyxpN3zs27dgmuPRlcqS5F6weLlUaAf7fLAsYFdjG0ri93iycG9-eW4wmr9JHGb8Nst2VMOKnXixH5xBvP2qF628ZCLQWQO6l8nVZCeqsp8J04NO2VG9nzbB8XXwVDaMu3JrWWaQt_GGIrGoOiMR0C7LnB43Mp5beYsNd_JG0ER6kgjDb7jlGkaZet1WurcjRp_5Ul3bsaiQPzCAXsjtchcop8PTA3yE7nCQCIDoF_OnJqgH8Bf_6kN_XojGq6cwElAUEzQFcq3ihjVZBppce118LxG1YAi9bOR6RbzSo-UaRb8HTla76RVClzQ5RR63Pb3Qj4vDmSYVdWIU7sFp_m_1_7U4Qs2Abumdkn3RPx3J-N4gmmIfXU8WhOYcHQ_HnbN6fWaZCx50SlmV9n514dQLdDOxGz_6uG81oyDlJDoLL3O5ZKnbC0ys_8E5e22AlUt5mGvO5c_Q4FfT92Jvzhy3T1rmKfwvArQQfy2sVVTJNTO04AX_OYtzDeDP-C9b5CQX0FA2xAsbkchFCv2rdGUZYA6uRpGxf4Sq28-VJmNX0kmlK6wsOEty8R_loSxCnfYReOWx_DC_7rfwAHnTZFIcpPEkFk2dTJoeakt5Mrx4MiiOrIx2FVgy3OiBrTUVdPa1SS00dmZ58nj1rDvNYbGMPYlGe2_SyZzWWvHKRQKsd5JkIwbl3NlO9dK8o4XyT4eVDlTqcqBiXxaqNauCX5NBpKTDbsiWnpiiKj6ubuorl8S3ev62t4luXisO1dDqm_p6PQRcW3YPdr_MKueg9Gq45sa-iNiZKTFa1FfzFwLlGE_e3DtpRG7F66erPoq-gubv5xgn1SxHG71_s9izNpOMwVCHEyHQgZFCkcGgKZzHsmn2jT-UDlH5DUBT4tVzbj44Au-dA1zsUunhN3MOjzytGrKmENyLwybKDDbc0KN0W5rJIItZ0lQuh6Fgvt43E_iFiH4cEoNGv6rdIHIXLZIAFO9viW-COXP0LCgNwv_iLZ3KoPvuoghtIqjqd2B1eWyVKELEzw4fcUgsWupAsvu7Vh2lY0aOmdwQL9gFrSk-Y6qEsomNoZ75xuoSOy0Qsq0jXTI3PfgQZPbW21cW_37OM_1yrtyvxx_36TfTqN0URL63kuTQLIo1gZsaSLIPqPhbsorV09vDYPIrt4TqzHDQF1C4wsDHMp6a8loLKGelgHEGbOvJKAA1UNu4nkBKI7eMzqpqyqJSW3HKae1r1pdHiMp1JnuWY3uwrReAulArgx67NJrxXFSet3GFBfmIpp_m8_iukfn30DEYX6NN6XspWDRkgIrz5Xbfya_QuUJQzFjOLbFoKTwCuFS2TmKxqeQ-fop7B7clIFoFP-b_HUefuoWMzjQcMX_oK0dHzJq-x0Yzwuqq97kSAmRoFqAgrkK6uakQgQ1jMXB1gcIpOwh3Z1BiLjqUtDrBUTdF9y21ugFgs8JiBiVqCSQ6xpWzxN4QQlhTX4dvUBali0fN43Vm3vuyhCzpt-PrO8efwJLLi5fEKnPKp-L37pYp8NNAYaYfvwlktPBrvxkaSMnGBKajt1zrtCZlHYVikBY8Zq40Qd8TjZgeGw28AJ6uttSi1PXdva7zVWfrLJQ0DN6ybyxOD-M9bx0p4VDa6S62E4-siIKlSl26au_REpIrFM-a6zol2rNgl_ufVxpQiYX90GT5gJEnHnaQvH_NN-fuoYCAvKV81rVBCKU0o_PN_yvSmXSxnp1LRfELim3FkoGhrkhAiA1lcLUDqC5CuUmVIbApCnxAshWosC3RGW-GGF-Psggw23v4mFtOYGOJbD58fcg5nzVIs6PLvAbd8Zita_HG5K-212MaPktv3QEC-O9oZgQxNsJ3VTU_ZLbrMtIcqB17bcyrGIok9zCtyE4p5a3vcfZqcSIM_j3HPbDGNMOzIbaNod0bFBXsff2MKwHhr1fTwxFIC-oQx__RpT5fAsCtrBsZwSbxYXMVFafbYQ87IRrio5PbGk11OxYWKQx8dSEoNVeRpVRIL0_5biQmc-rk4QlF9beH-nLT4Tbr1B6-9GnZN6S381y-245lolGV0R8iR3atNaU3aKd2zPJ-C4odceQ-ZhrlFiL9csLrcn_dPc9GP_-XR9CYP1E6T-I72VrbZyhguohJiR4n_H-J3dmI3hl6xgpOKsxYGs50Y7NjcFL-Y7mgDlCQfu4SWIvdy-0cWMGSFOzN9c6--x6AnUo1E0vwwuakQ2ShJTePKI9E40ur5gEbjbnMCdZ78_f2q2aL3yrtDlbyFc_whZnEvEF-tNT_3zBDwv9d3O3SN7yoEho8t7Oh0j6hK2MEjWzK-7evw2mpUJSf8isKx0oi-JCobgP1QbfKPBocpa0Lfo40OaXyz9yDcKw6Oxnix-HtfMmPkOoWWyANbdSea5cGzmRZZzfe0JUh46UB-uNnwb21I9Q0JzDUrJRP9g5SIj8Q9XOoyw5aexLEaRwhpMnpk-Byc3CGvK9A5PfdM2ezLnPOJiNETsDfIKGX6xbXvLztMnWx47bi5a1xdRFyMSyBVQvD62CT1wjyFz2blecL44At2HV_c3KGeB7J4Yxp4NHurGZC4mE69b1r1b3cajy6eX8pG0Z8jaXZvmOiBddOPPyVq6pZnkbKV7XwJdzmliFAx2VVYAmRZY8H0_JyEzyuORIWGuwcX4mxstWpRlGWf3lcG8x7tghKJSR6E7dG1PTesIjzXFqrdsZ-9WuKRlOhrAfAKiUxP3-hQb8heIxy5J2_Fz4gVGMSeD1tuFNjwB56YZksJxd6Ds2s__ldicgVvmXNzc0hbRrRpLQ3ev8W_rKWvVNvcBLDJmU7bP_nE8D_8n9DT7wCiBR5TIpINU4qOqZrrZcQM8x601E4dBop5XLPY8wJRpM9ejx-Ol7KduMJlaRF5PFOKwgFsKi1CGzx9xnSrow0firLiOL28_TeSNm4XXUQO_GX3n2hypekh4rXtPE5hiRQqLVKSRUrhcz8l77MvSl02U1ubvoglvZ-PRUlKvigv19hHZLrVQFfEjZIPj_oBKSzW1nSiejVMMHvvezCV9olz0dc0cu0rS93G_THp-dOdBQ3blUwWIsYLAlyz2J3gPG0_9j_GethE1ytkmXkOrKGoa4WyAtVc2P6PQYjD2QRiVA0PwDKOdKtkW68Ov_rGGfngBLZpbB_Hb2auqCPlLLz71Q80a3xhU4ooS6U3ou96bdPvj3DlcUbxVCHZBaHNuBqQ8iYPnVlHPKQ8TDgEEvuxPvhoqIzuOG8WXk5VyPbeviPZxKPC_gmGBZ5OVI1Z-l2R3dg0V2yoI4DRgAu9P2rAoW0EdL3jVRYFLNqbPpKSI50l6-zVKVeM3Qcmq1Afo7KKAabvGuXalDPREqOGy9DtWzWZ4Bx7IIq5co-o_2YH6CadEj-M9dm-eZB-e1Jpcsw381EE7EGDY1Xy40Ud6JZ0b5_DE2MITGR5UxD5P7xX_Igce0DN5jiskODTxILcZ7xOBEe7Qp8HO0nKl-zC6lVSXmeyT2j9QMJl68aM9fELNx6PPA5zO58qMY_iD7eZfqUOTxM8u44_NDLBKzfyXIh6QSmUY0WyV-bcNAbE_2BTZZddF87itAGoHqEw0Usk1UQotCPsIw7dS6SYrqhzmxNFt7o2e45RqtmOJQWlNvfh1RQUli2BuyyRJ16A2onA2hfyCDVpBVDyNIdPA8fFSBXAgZsaYEBf0b3wA0UDNUMNanz3PzOyxPJVso_01kRWAp1QZWmCzHHm53fYzL2vG7TZWXV7-hTqjy4GpbjqLYtZ_YBqgBWSxDSQGixn8nwAjR2d2O95WIe79dqRQK1qCdsVh3Ub3y28KOtGmn6Sd1rND3msXYIzAikhEOg4tYH_R6bY2Vr0HTeZ__zQ2bDnB64G4vGZYmCfAfAFcwrhV8DT5tXQTS7-abY9ZT8uzgPozeOcoQGTZCA_s8rk_Ox2b9dRYAjrbqhKSCysYok9JFOnocqh4xQcXXgoWibCWb9OJKnDOTEoQa_71GitupOME6YQ-jPvKwWNJq-CsOdJn6h54WpGYMyXW5xU3HG7cARB1xIbhzdfRIrPnvPqARbOu2kVpG5OlHSzRt-E7q2B7RN2SDxHv1EWIL35x8mTOqJNb5VQm-B8M4VQmSZuYmm1_za6QmfSfSRLQwP1rDxjLiarsEdR7bOk46wRjINkb4h7LOrgv8tfRLULZm3r07RurV9xmhad7FZQazGmJrBYXqHbvE1XpbDreZbv3Si7B0jn1AboGVOad2eOa41iMGLcvG7Zlx1xPczO4O7oKHSLj5-2Cg_mDJUlX6penpdchnXW_izdvQ7zXEA4HstujhcIJ92hdU5ONmGI6EOku2VL3P9iMXXEHsaoaIcfyG4EDcxg49xQi3b6cpUDrZBJCAvlJ9MpO-YFPnH2Kq4isa2XOWmAYoyO_wp9-eFzVfW30b4LQkr7_ylnM1iDewZGDypyU60hKjpFECJMLbvcHJF3iZEPeMfYNGcYQNimlj8ih45nDRn-dKWzLTre2xiDOjKun_TQIgCp1NiRyhPM1WYnlIWgvD3HIB75tv-xgeXp9YQMyEF9zHUakwhrvb61qfT9lk7upbVTvZfccACLhds_V7bGz9hzrhY8Ic-tWilIaMTDuqPMc64N7mHd2OWe9_O5bEgBsTeSBHNOaizo9cEX3QjzYqF8lolHmicEL7zKrX4NG-a_6__W7QUaEDSswyGaSCwILSk1bUxjo8xmbtPx-pcFfjS-1_7s1lKmf4QtLhR2d4rZIyujwI6klHU7fYgspB3JJZ04-AtPxBzvBE4M61rSSpXIEnlu7us2B6cJCjf2j9mmRD1YKn_Ggkcy0Mgn5jSVif0hNOrXOyDT_9ih0cZoT3iLN8tRtA6BpyZLMSlevGOxamd3QTj4Xe4pB1NZHEkYgav5rKVh9lOtut_N8u2O5F1rbOTezW0sgMTId8FrvdAXgIDHVMuMrWAxUTnI7Ut31sq685_zCHUowrf30J0FKWiwnCX5UY4W-8HTqKDzyYVPseZqTfA4HqpfASCfLaijWQHm5ltMYSY04w1aKoIlUWtIBixIfbGdxHa45vZaPOCJ6Tef40WFpLxWe6vUIJLREUmb-z87Z08Rj3sOXYNA9DopBYvjZEgPj8xP2h52_MlQK0x5CQQnHeic7XRHko2WwNW3YqWaHotOoKanNEOwQMDo1-IW_8J0uHDlC9QQNmLV4KchTswEcR-G71A6UhjXJH0S4CBEUvcEjqcyilBa1v6akwX1OGy7UPQxa4HXyiCnC4hJK4cm5iKqUsNexDcAlKRryzyxnGVi3T0aB9Idf1TzLkVz0Y-HcoI5yva82GrsrazxsEZn0CVSzU3Ph-nn4tgX1lN64Hlx53hDyog7AO-dIl76p1eZsvU-9li84-VQjcYPnmkbXD8gwIWVzjMryvWYqoptV0bj25UTxFxgnD00meje79P2gSTxFGbiCZTT6TQ7g14_PnPQOi4ItJROFM8UVY4ap0OfGqjzbXYGNY9f9oe7rKHGPL2Ne6Zog_-6dAUaEYcfng5JrdBgALdR_K574fI3x1A51xQ22Dz0quLMJjidUWHwBgNeeLbFcZQFwWckSAZaBSQF_AXQFooBEBNfMZ-gXtJ4DiIxnrZ1t31NyQFT-z0wBdv0T-6TMiRR_5EhSZf8EH4lAet1pGqyhB5bFukrlEFZXLDUleTj1uHxzuDvHzNqCvCrye0ncoZLhZ1wibRQKk06jAFKagvp-H36Ge9k-iKsVhHcX_bQ6IVI7fBtTgWHFaO0Pgcsmzrfg-_RT_T7PlOKFyrUh76pxUyViueFviMJ9zu0alP9rs5EOmJs3xWfeimUshaq9fWTfYmS-dES0Hw2dk-6FMAOjkz5ZEHt1PrcYbtq_LEjfFN8w0cLQL3wlJ6fsr85PI4Mv86uN6ImSxjnq471CBTZDc38IY0qDgNmXCPpe--ehrU5v8zDP4LmSCi2ykW0qaFOh3bmQmLoT7uhXXqdUbubHfcTqKUhNPl3zjjJK2eITeoxrpkKysGxgGEdmRBRd8HOkaQCPwVbsGLY3MTpDzyRuIuLtAKzrClHuh8jfXFmP_z-V2H8ywVNfWVb4huspN_kulJALhFbRzLf6e5eKzimjJ1uuF-fF258oZ7g6uOObsBtMfEMVla8f4kaJVuBrfjcQDodsUd9sdsuchOI6BmkN_Z2rbLTW-WuIUKdS21S-00ed9em3QNHDQ-md7W0c4NGkTls0wF-UAJ7YzL4knV9-NE3WvIw-T1i3qW1r7ahoibitekZtOFwa4YgZt3aX47nsIY-olNVhV6qTueseViiQqOPlgETpwj6HZlsj-AU7OquQpE43Ja_SQ8OLWJkpbXyEdHoYetZfFHQnH1RjLEaSVGQJscUSqmhV59NXBXXLouYMpfrLhLoLGfSSiNTxALTrfxeHaUVft6naGPjROybBIb4EfmUqyX5U6ziUFqQ2dPS_FlZUOzuc-3w4mrSBR94LK7d6_HbFo59ZW6JgzlFvOir4zQtErqigYYZJFNgAD5DUWDq5NhjFBZ5Yc9fdDPnaTIeQJESc7jxMVs06Bkl5QjXe1vg5B1P2cqlO9bBDBCGgGWD36zrf2MCtypodtDBgdJ7cvHrrJJ6HXcaBgZqknyMOM3kjTqFtASz_qgEcfIAo8AuDlDz0m5tuZ4IlZh1KA29_55a8rAqELXNvf3YaHapWPv9uy9dkRAXyXv1sVqLU_Aq93ZnoLrAGX7ZbXxl2urksKNd-BkkXiAN_girjN3qEw0sg-AsIFxT3qiSUlhbu-gRFV4pMJR_AO0kR_nNJBuWUopWpLDPkXGdpbJwR1VVG_rX49cbFPjoeXHhlO3xI0m1vrQX9-g4heVLKy8ZSpeV3J-q9P_6y8o5TMyC-GjAMYpmUWJrEPCqcSl2NmHy9YuZl0wvwYSXVXMVjKcj54iX2i1eWM8N4PdUKwq4xmlTlSaU28Tsm_8bzrcyVH5L8K2vh-RzvX_WZVwaOOY5LHPt_k0YDp655ntM4rMnpCyN9Sp4jWR0FfZhVcXeanXv8TBJ4G-3vKIORuhKiGAnqMhN8hvWMUiyGFTEFbM0oe32alr8JSe1AKO0BukYMj7SJjj84BumBJDM1Op8BVrbyqcVsESgPyktveeZ5iZiQrQaQa078B6_4w7BQU2Nl2idOy6tVT352xnysb8FhC-nTxXG6wkHcheat0waJL7Wrtr73hQPaCLa_DNbdzqc57QmpPIYu-Pr3yKWSIgoh_oPeLNv5fHpZTfUoJInf2lZ4kSMBuA-87DjWUBo1rN258vg9KyOHI85kbgjmPamu5OEA0OxdZ5M4bTT47NFGZ4i21BsT6N40jmNnOUNVDFzG2DoaAWcScsRmTs9tJh7b8OMp1GPgBiy3m_x2qcI-2x8aw0JgXPdW4jvgqBrgDI71LuoSJ7_X2BhU1OAZyJacpqxHjwt39D_SOdQY3Ah8gYdoY-CcaBMo5pP5s1hscuQfDlw9EMnuG4HQDsD7_C1J3o2bDXb-sV7DO9gZJ6EA9zfumCJf6NXn0f1SVGvMLhWP0Mml4rZI9atDiCmqPYIM4XCjRMX8vOh3LRvs9WoYCUGfgliPKsKA5f5pvl5J5nxzCnQD_8HV7jDRLYzfbEfxRDFkyph9xWAGwVtOnv4DdihkiTf-zXOIpusrgb_WKTZoF5XYOvfjxgz0MMENiJCkM-Nr58xqy6xjvqu0HxyZ6XazN2Bkdly5WTnD-R3kTNL3dIglPh4sK4WShAIOhVQEfqi7LD8wDhB_ZM4ddoBrG4pA1RW1eIOppTOHPOKizzvoB0UH9YyW3Nl4FC0e8GLWrad030wxqe5wY1JcvvsyWZoGrfy8KDH8RbWa57uTsBlm1jSSAOFfueFwKU2_13kppwEDKg3pdwnJDRIytmc9G0H35N6sTDrVOMbNXu1yD8TDGf8C2zlLrHXLEesBpbjQyUTC32YXHLvVcuFzsr0SiBcju9T0dk12SYRxMj-FWSrFrIaNREm4QCehfXXfr8BeR9UqFq87g6XzTY4lM_YzK8M2Yb29LTIhUuifNbn7EMsZQln6QA9Fe1jlXO_9pEDmYxOKei33zz1VjMdd6ccMx9IiUx8V7Gxu1ScMIIdvQYia8TyV8IFFptri58KVGOsNtUJuQ5n1k-VLnkkXR4Zwy1yq4dHsa4sL2-7uPTckSy-iVM_gn5kdK69KBN7zWXGEgNBznSfeIqHCSuBA1XkLvjRO7S_6pMh6RRQbvwmZoGXqKvcmALf0yQs9bo-NMymdOqtc0QacIKiOEV2PLAlP8XWi0a3hLPbHuYsafSqkF-W40hOYNzf-zC-tpjYtK8B7cFXnTuuGf-w520j9gIii3Nft5jjBHpSwFpIqGQvCi7S6FEZQS6YU6jTrKoFOI4gOGvj14UpZkcDC8eHmsxVoRZJG9oxfBodrftEoL2weZ5vxD0mhCTnIjRdX_BLuDVhe1qH3eUTzN27Agh1YP2kP_7goOI3qL6NjNY1V2Sd1UytXs2veLf6our2oMxyRDhDJBEnTnyXnYG1CKCZYlb1mJV1_6ap7eOSZNBCYIRrsOWIe3RNkiFn3s_ANhWTQEbNNJjKnrEYxtLS1-777S9KpqTXacrPhE3mOuwZysIGFXT2oXmtW6qCnNC4tgF76nC1ACwB948zjGlqJQbv4LwUUbRsMQ6MxEjTEhQ_TbiYgNclOLzZJXTpNMPdp-DIg1Y90zyqheO_Svi2scSsycUNUlIOxqkcO4-ykKogP11A91HodThsUXpHGAhHo8mE6tX_jgaSDf7UeV_kVWQFK3GxElbryTFAgJe3arNj-ABPJWdf8SEZlSR84tOHB4wgwH09Tf-xrGlVCo5tstouHIC0iRQiDvTglljV9kcJ4wa8A6gHW-xR5OneWKqAc5rUwnTQhBlgQiG6A-CpMWfapPkL-HySF6dgPczfkpSImMNrmlgn5iC0zZIotgRVZZdsQMjiTj0z8pQWvAIsNrtI89DYUR4Z9y-894DZ3Wj4thhkzby2Qo5W_l_Iu4aV_nR1lO8jPBnF9DgNnjd7h2PbPpcKaSG7jEm5qZutrx-LMlxu7H88vJhYcdK8lm3sGis35RMYkmjtJPlDwAbWEJPOhS3oWSZLCelE2QMzvGU60IqKFQ-lcCsgYKTLstr5uGvF94rY7DQbL6gv5ggrN5vwGvnl3L_wtAf6MNSFzdYifDXJsLW_PuXKqSidsHNEtOyMDTFqwi9-6y5VG1vIMOCGrqdXicRO5zRIJDNoephj-kJ2wB8TudK3Yib9SHbW14y5dAHpd-xVTg1pr9WLE1QQ-IIx8hFOfGC1ajT62vS2skDEHFFxvXxkqPdYn3v7AKaa-w0tdnUu89CpFAUCOTF2qJPjio8MhjykyKyomeSkpNm35Pe3PUxjHXndyK9WsI4Pke8HYPBXSYx0NauSWAhKOQwrmrsOQW4IBvrJRe-ljuyzSwi5savpLgG-DKUEIMOPEyMVPy9MeTQ80PDu314NdlobXH7p2hCtE3vNqnWe2blR1ooBdcAx028wQI1ucdF3VvF-4GVtWS2L9VtCR2pvi220k3EVT_L86wTrozmCp86lSIhfguq84dggTCLT_O7NBSXJjrGMEslE5XVwTSW-NSiJC8GCW-JQRBduhpdxug-MnidLBG19Q3XPV2wi5DE7AQpHPhGXWXkAGZaiFkylA4NgreWBkvuuquC0FyeXUHCywkTZ95zRFaLJ-qD5qGXQ0nWYLZ3VEwtMhQaLhwsFamNhjZxd5t-my-Kw61R70pud9Wd0jWoD9m-ittzXDRYQycgk3WsZB38R2CZuuiMRITrDbtLVpBK640_Rm27L1_yvzmo_oNnj3SAp3bYul5ya4jgLt-ueAVrKqvizDtyIdS7xSXMlXtN2AQnBDH5YFL_7aUAZV-daASmjI9Rw8Dac0O3iHfoHJcJYZDhFY7ynXI4JTquYLCZBUTsFHb_6Xx-SCRs-S-Rq3ltOtYwBYaK1X63t15u-BdVMuMxAw_qFMjK-F4KzH6xf9ApkR5pEvSHx9OzlATR-h7237rwQmFJYlATeAO1jfODeSUnzTjWf5zfZNRrvYD7q5Jo6MiiB4iZMrFvpbdeo_75lsGtPaJD3oK2b1yRkO59CyIhdsllCMd4QrYj_I5qjUmiLPHhTGfjzi7aGdbMHJfaxWWEzm7RYYqT1GLHMA0a7iY8FjnOxVU_sJi2hs0pjbXWv7wt_ICpB60SS4dB7OhLulBW2eRwrnH3JkFBpS6hTapA829vHvp-sIv7Hd1m9n_jTWQyi9Tvdah-ukJTdVLz0_S04B-N_60qSRuPlhq5u7hOUAcVf0RLk_VugLxEFTFsN4CQrTZuoSm5tMikSgVhgz0wgnsrfaIQ1xI1AsSMW60HvSoMGEcsYQT0PG9cT8f538-1NPbjvb_knWXtMd0MTySTPKjmhKlxpfnv3vxMT3qfsut-kY9LfjMGlMP0qYPg1yVplMfPWmEo6bYEEZoqiGPhK8G5_fVXy23yGseM4OZjj8zy8_rLCC2XGpnzzZCouwnUDRwJgTaDFDenJuytixk8J7_5x2Tm9YPw0wAxKSV90RP_TRkk5ezmk3_usEBLwRnJp1WMlEFSzbblU_JR3NYIZwMb24ddL2hOt6OdrD-Mb6t0Am_gbJC-eQihzPOF_Pi9qHE3QXLaOwEd5VEiKrwujx29w5nLPjNpKmtkx87GyTK_aWa8qtVBju7kN0zBjudp1J9zQwCHyye71TYaW7OnoKTh9sx_2Gxfp9_ZlY6YerMrMParXBi1A3C6a8AA8TtpiLjYKgUyA52ntuf2CCzcgP1nsMQyCOWGMVk_nrjRvbqleMu8Fpdlnp3OwJyfPG-amyoqWY9oHTXadXLFXqamxaJtoXJaiBxznYgvO__7_UzVBdn1r9oAfGjK2D2ot_r8U522tGhD1TOF96Kk-XmTycXQbTisSJlKUpwEIlrir3K_nTrEcNZjDOPKMAczGteu4ECTesN81j0pDdkdUZfg77bI3Gwmyq8okkiaLsNwB3gTyXXSSl38PvN7q1bIK7K7Ot1p90du77ZjYO9CLgaEYiMI2CrsFhJNfo-zmX7PGk-EBzWjuGusqj_MdRm8VgMgKOZBlWq4od1BHCztWmHcpMu9wHOUSd7pAxLgkHISp5PJXcZewTlJRi_ud39znNPrmbx-Y0ght-PGydozBlljipspf1b2-oZDASYUiDUU_aO9alSpbynV-v4slqHIUPeDHIeumqDw6Ggi5yoL5SBR-Ah47Rx9gFUSf00NytHHv4CHbvaAs0p73I7rtATEQm-8-35B1WITknZt0n54RNWUAT0Y6774XKW0r5zm84BwJ--rban6_FELDesJECRkkpYSGECVHHbopQ89MrWbWjmax9U_n7xQq_DCjASLvPNx_joZpCjpZxJIA7NTBCZv3Wm22kjChaudOkqyu8lzz_tIe6X7uObKUL7XtmbEEcIlpOcQk-57KzqtwvO8H1yyql3mCANkpMYMHBk00-e9Ybb2EZfh0-qzGqZv1T_t93ht_ZX-i0fYTAn_gNI6hWiSd06aj3Uhk0zm0ZQhUgcq_BjaIZC7wnbJTrMLb2v7u9NYciH1CryKcOaVKUKRI8ZWoFRAyjD5r5ZV9jrm955jP_PQRsIAbXF6H-5Bpirobx_nANpFealtrp-6Bc4X8ljJ0nPvxPllQ0fpnzlXuF8WrrOxgTsStpnOhUMti-_5A4opGCre0G9UbZ1clPyYxajsJB-88SgiS-3ER-l03DcKJ143vqcL4-jRBHwx8M2rX564UYeUXJ6-n-g7bJGqazY5eh7A21s1JKaSGG1upM_sSHaHIjDKVC7OP82lzeU3rm-GIa85HCFbiKeQT2KkKaXWP_VaxjtyPdWRR2C6xLb1tTZ344uAkA6b5W6ixGdxDSA4j2GZ6rXoPcPvdnm9zzK6f45jQ_R14zKpNabYz9qUzUJgwMAiuUCj9QXfVGMrG8SyKJwrbLBdX-QlHgZri6UtW9_xutEd9j84ajAR-zyiYabZlZiK0ziurZzEP0bAadIz-nueS9ctM2Sx8ZAd_077ZIZX8dYkeCDggJ09RyClZ8a6CWQMynsNrWa6VVsJnGDeuzf-nGRFniXAr2D8KPEglX8VuxK3tzxLjzfD58ScBNVokDbl2AsN9bpLrnluxdW-P_I6KuW8sITt9-9GPJbKhT8jgGqT7AUbyd9v7fop4u-hqPGd7_AJGk--Kct_g0-OZdMtgdtz5a4L1EUyfjAjLPfVPxmRqrR8aDyHsYxXvDf0zby1yTh9WKQPp4EU-9hSUjBlhwUu25hcU33uwF-jEYfdWZeFkjhAZvL5g3Y6-zXyrF69uQ2zF7lGPKXGFvxCIVXmtZVsAd3KoXGQJTMt6YrGEfyd9ybcbOdoBYMPedzj40jIHeNUrjlxFrCCkCkkO41JTFIH-q1TyLhfxoT5OmvCxyexqqBhJbGbqbbMVWhwcpCpJYDu17mhTipSZjvxU6-oAiwjLw-gcRZgSgCYSwvdN72ykOrtI06annVmr0XBXmgqj_gDfHziiGfPwkD1fwRETcHq8HJMgOdBWkfneFJ-VgBMLpk762hPNvyNrrht3nk6cfHPqmMqo5ZpDrhUhudOVJmW9FaM0juULI6VuZqZUnVlVDj6Tj1sF1cbApO_yCznJ4e1oVv0JvYO4CNUt-b_VxbkrdP66C_GMtwNk4C-MH6WS3R6oP90__WDAqc7Hop76cuUGfCwCqAECIN_FPtMHrIbKWDlCshjjsdGyOP1cpxRGIP1WRajB2bag9DUv4eKSYOYSzuC1-hCMAig7EZsWTSzrW_CQ9L-3ShKfq1gON1HDRI-CN7DHnqyHo0IOe1n2pL7DvT__Uc7Wpqw0yA55e_therfUiMg3Nv-uTEmkMW7VZf1hZpiG-QtPKv172trwNTaE1eo8qSqhTFXRtzGL40aXKHYQmC0cTUvbxMJEOQZk9YKh0WQ7fmbNAqKM24FrSfnYl2sBPcTlslhiFvGxmU0LUabz22dYCqdwZ3IP_UQmGa8AGeXYhStiUDjh_fnuktf9Qi6m4nUFvwPg6xo0dzqEbepnIO1Z7hUnWMNM3En3aee-QmTLuuY7W17gP55XqXR_yDhcO3wc4sUf-91EoEixRAShWTvtSRu_tr3yL9WZUPjjR4A08Y05Agznls41FoWvdZw2XVOtbavD2SuM5_q8-wf_b5BKLReSuYq2yaBsK_S84Typ29vJVi5JJxsGxh-PvBYUdXUh2VcSRDAc6UyUkQ4d7rx3AwSNBvoVNRJZIvciFTD6GcOl-B2vWxof7WjS7LyYpx39AjT_fY_-Ah_igw731qZnqRMYmt5jHFNErNAk1NXF1nd4oCHDwSXaZWK50RUdcbcSoez3pQHZ7Pkzo7k0AaF04nvUso0qDCCRPa5SMBVFbdTR5HC749ywTJgpXXLFHcdoZknqre020zeKS24gkhjhrlExgCZmgoogYbwYqvDiOE2hT57-yIAuObZ1m8ywcfmVCUn4yJgcIHILqt4uIzZfpU2M9RS-baD5VA8qhubjH5M5vbRzwORLu9rpxV-LrrIX8lkYYPtYIyLrJFOiCVQYkr_5ECNVgs43QxCk5ZibyDwe2SAWkdPDLd8X_O6i3kblSMm-VQAKW8HxOcPEuSecvqyEkCLq3Z0yOD09W4DUZTU7SQ07yykrHI1nEyieIik68eXh6Rqw6CMmQzMBPQ6v5UQFTFHlO65--EfDuZnJUXQ65mab_Kgd0pIc0EQ9ko1BjbgujkTjAPXs32AiZXTFx4i5k3zrQw7hEYP-hcAdZOrZ5ks2yH3n-MrWeYsgWbs0pv6d4BJtjerTlNFdnbGcXb0hapnniUVeoQAKrhdSv_Z4fkHhb26Fa5EpI6WLKexe_lc-HETKovFSNglJ2PXqDVQRWHPSY4aLfBXwVXp_2TN3F2Mc_L48uwymkIprBtNXIXluu3qlslztP5TbeDCtXoI3peHAgf8Q8fwaujUSjsnf2mv37m_8DBEHInlotHp4uBkgURGvpNMCggAAKx_zT88u3_Ohacie_exYenwrlTfbGoOMHvS60W9wRDvAVQnw7320ZD4Qt0k2mzhPdmn9FDMYmL2nX_64e24Cevtwu5-KNqyvDkmmMIrhy7XpWk51xpOM3gLEBMZDgAWhPqSXVHz32knVJ51tpxiruwlmVgGoSe2jGGvlJZoSmn03_7wK-H55bQTEUpYeofzO44d6ufqDtQpkcAA_kBzXYJYG8jp8zIEHGECwmz0Eeyij1wl5GWTQrAPxBsWQ4j7HAA9fSyZ7wHPcNMVI5DWM3--Xcb3yWR9cou1dCyq3dpS6RfdS9MA2TxDrDWTaw7wAnIbha3B3xoyZ6Z5bPyr-MfOKY5BjOeHPDIwh9ZDH_bBoL_aGmsVtKjsvJLCN_PS53J-LcFbVWDfkqv6zgQ9dJwXQsA5crC7MCyfukFdaDmbCnVmk-Wu7eho8lULLT6B7bBO8qoWBM1D0fQez14b0JSmqRNX_3FdPjNdPCkcimJylqGQfR7dnZcuYCZxeCK9xv50P_97ibgAC2xDg85XIfI1Xpaq8yXsTiG3UcFI5iVXTmwyyQPOwKjNABUBAm9VPTIpqD9RzEtYFGMssPRx_k-Od774qrn5yhL2O0d6okNhotP5pD0XqoBCnBgd_TQCHUaoJt8Qr62C8ZL8pmgsueAwBWwr0lrxiTtYofmUCsocmJqyBAQABn4gppo1PdG8hUJCcQJnNDssoJDlrE6H30HTEoQnHhnaSyU22fsZgAqdTo2WiZeIDL8uzOyYDZVyAyPenVnBj-71oNIpAKS8fVMTrvmQ6nL7NBynfYNdDNTDiYY9U-O5tMhv9OQdNQtrJfuJdKLhecyK6rsOC4un0DOwcnn-ZSxTrpYxNMPvvxRnX5rcN0apa3oYBs8BQUOW8x96set19bqgM7SIhM38QcjQivgfTOlBZhN92gJXSxIi54nLKEYYqq9oyEsDZ3lIu-XEpv2qYdtfrp-UHIXG7kvwIXRXHJZhECjMcV-PRvwXP3j9IY_0URQJII7eCpGhSfi2VAsS3ABoObkzGlIa-2ULEIcTaX-k4b5SsYJqSdaQTRvMN72xKL4dBuPWkmSL5rG_xo4rpv2eFwHhgMo6cAYPHLsWX1vOgr5937BNpAzct5bVvaH_npOm1JZs9ks1ZBuQyqtz4y0EFcN4t9hoSmSTSRvT4MNatF_BIu2U2iKBkTXgFHGKk7_jPqC6WxD7R7XN_rwRdk5vV-Ac6DpGAheIijjCAcaOcixZ0NF5qEYZPxbU7BLLcLdJZLDQxkCV1HtqA-efe06OKSmsXNiEBZRZiLNQ3t_qny_M4BEKRGieXYRWh1qUnRiPJNPLCErMfQTWs9mBpYYjrk3KzkWLybD1xdekp3IeKpSU8qiTCGLrubT-9cwZGmXI06-CpgKFTMDN9n6qnGyk6hK3hzZLIhmTg5JobXxvkHGJO2MY303pNgT5TQA_qMiA0dm5W0aDnCHfSEPDLFC8uwbG4At9JWzv1VOvLYvW1SBqg7Ndh97ijwFLOMURrEM905mQDAzGx2avsVAqiLuyHAelDuPqK4EslZ6_y5BsL8cxS41Q8k8P2alXpGbdL0bYyTIAixyUjkx0uCQ6AfZpzikm-rWujdUKJROHHPBuWX_BEc3nkThHZfdyfFCczZT1Gosq11k9u5Iqu9N05PCHdkNnL6P-VO8ADlul6zjiNjZfu_8wIYGUI7__o50OUafWDWgzsta8JSBCMvynOxo8jZZklV3pvbgRCbRAibnHI59SrcrV0_1jKacUXaAMnFpQdJkEB-Lhhz7qWryeGFXbOONRpW5VlQr0EUJXkwYxTaeSGAR_PigLxIdIzWwH8UdKKuiWvlQ9M64xiCKv61_e80Wp_bjETjVtvj4qLROZbl_gbWi7hhBtmS_WhM0TTJPyYt3sC-r7B-P62H8Xm7ECb8-MMzKOB3hn6gySpOMs4OXZnWZzVLnLvKmINjVwJVv3PQ3vJ3l5c_nUALr59TZLiJjpopgn9-4_i3ZqUrl64dmxwwV4slS4EX2U3IGDxGTqYbqjHKM3EuzDMHG6Y70GlFHG8fMt-lariTD2S_AQ8KCF6r3eRLZY75g7Vf9uaDG9jKRKNzKl_h53ciiTOHDoMd7Qw912kz4IDUPty76R0rvSkfiLe3Gi327xeLl0HlkoL1yGzjoyXwtHIzkU_AApfmzw71GFvcriikUN4o1oNT1C0eIn-eYj3d3z_yFMLOCd6d2pAqFZKdwgeO8sisLTYndBp8leKb2VCk0fvuyOXvaLVWFOkjX2_JXA97aIehc7TwxWLrX7u8V5O61--g6t6MoEm1E5hIcdcexbUVxQzpqSh-qHe9p5Q8kVc1LVpGBbPid6qJhEj4ee0wSx8zdoeoleEzL2ZVNn0CC1W89JuF9D2noPTbeQmS9o40BMZQ0OAWLIhph1ivzOxCeo9gV75MlwV708L4xgT7nh3ZGKWf4rwFmxzgjdlCaPhDoQ8D2XXSTEjDzA-B1QN9Rexd2AOUQ9EkehwZtrxHxVKLu3c8L_1K9kfyMcKAZZijIVvtwb7rYVrHT7WT-jL6hmmmSJDFQbAAyy6zuj1xeF1QBRX64Z9lK2ji7wAN4he70VcYXI_XcNeuQMntccNcv2JjhE3R6ivcoWJO6SD4Htgy0Ad6021x0A.rsQjmbtE4tdU7Mf5IF7tsmbU3mkvgYIbWvLsQWM3e2Q" - let (receivingAgent, receivingPluto) = try createAgentWitouthMocks() - try await receivingAgent.recoverWallet(encrypted: jweByOtherSDK) - let keys = try await receivingPluto.getAllKeys().first().await().count - let mediators = try await receivingPluto.getAllMediators().first().await().count - let credentials = try await receivingPluto.getAllCredentials().first().await().count - let dids = try await receivingPluto.getAllDIDs().first().await().count - let didPairs = try await receivingPluto.getAllDidPairs().first().await().count - let messages = try await receivingPluto.getAllMessages().first().await().count - let linkSecret = try await receivingPluto.getLinkSecret().first().await() - XCTAssertEqual(keys, 7) - XCTAssertEqual(mediators, 1) - XCTAssertEqual(credentials, 2) - XCTAssertEqual(dids, 5) - XCTAssertEqual(didPairs, 1) - XCTAssertEqual(messages, 8) - XCTAssertNotNil(linkSecret) - } + // TODO: Removing this test while KMP finishes Backup +// func testInteroperabilityKotlinSDK() async throws { +// let jweByOtherSDK = "" +// +// let (receivingAgent, receivingPluto) = try createAgentWitouthMocks() +// try await receivingAgent.recoverWallet(encrypted: jweByOtherSDK) +// let keys = try await receivingPluto.getAllKeys().first().await().count +// let mediators = try await receivingPluto.getAllMediators().first().await().count +// let credentials = try await receivingPluto.getAllCredentials().first().await().count +// let dids = try await receivingPluto.getAllDIDs().first().await().count +// let didPairs = try await receivingPluto.getAllDidPairs().first().await().count +// let messages = try await receivingPluto.getAllMessages().first().await().count +// let linkSecret = try await receivingPluto.getLinkSecret().first().await() +// XCTAssertEqual(keys, 7) +// XCTAssertEqual(mediators, 1) +// XCTAssertEqual(credentials, 2) +// XCTAssertEqual(dids, 5) +// XCTAssertEqual(didPairs, 1) +// XCTAssertEqual(messages, 8) +// XCTAssertNotNil(linkSecret) +// } } diff --git a/EdgeAgentSDK/EdgeAgent/Tests/Helper/MockPluto.swift b/EdgeAgentSDK/EdgeAgent/Tests/Helper/MockPluto.swift index d2dec7c3..38e0d116 100644 --- a/EdgeAgentSDK/EdgeAgent/Tests/Helper/MockPluto.swift +++ b/EdgeAgentSDK/EdgeAgent/Tests/Helper/MockPluto.swift @@ -47,6 +47,11 @@ class MockPluto: Pluto { return Just(()).tryMap { $0 }.eraseToAnyPublisher() } + func storeCredentials(credentials: [any StorableCredential]) -> AnyPublisher { + self.credentials.append(contentsOf: credentials) + return Just(()).tryMap { $0 }.eraseToAnyPublisher() + } + func storeCredential(credential: Domain.StorableCredential) -> AnyPublisher { credentials.append(credential) return Just(()).tryMap { $0 }.eraseToAnyPublisher() diff --git a/EdgeAgentSDK/Pluto/Sources/Helpers/CoreData/CoreDataDAO+Combine.swift b/EdgeAgentSDK/Pluto/Sources/Helpers/CoreData/CoreDataDAO+Combine.swift index 34c18c5a..aa305f9f 100644 --- a/EdgeAgentSDK/Pluto/Sources/Helpers/CoreData/CoreDataDAO+Combine.swift +++ b/EdgeAgentSDK/Pluto/Sources/Helpers/CoreData/CoreDataDAO+Combine.swift @@ -52,6 +52,19 @@ extension CoreDataDAO { } extension CoreDataDAO where CoreDataObject: Identifiable { + func batchUpdateOrCreate( + _ ids: [CoreDataObject.ID], + context: NSManagedObjectContext, + modify: @escaping (CoreDataObject.ID, CoreDataObject, NSManagedObjectContext) throws -> Void + ) -> AnyPublisher<[CoreDataObject.ID], Error> { + context.write { context in + try ids.forEach { id in + try modify(id, self.fetchByID(id, context: context) ?? self.newEntity(context: context), context) + } + return ids + } + .eraseToAnyPublisher() + } func updateOrCreate( _ id: CoreDataObject.ID, context: NSManagedObjectContext, diff --git a/EdgeAgentSDK/Pluto/Sources/Helpers/Message+Codable.swift b/EdgeAgentSDK/Pluto/Sources/Helpers/Message+Codable.swift index b235852b..e86fbfed 100644 --- a/EdgeAgentSDK/Pluto/Sources/Helpers/Message+Codable.swift +++ b/EdgeAgentSDK/Pluto/Sources/Helpers/Message+Codable.swift @@ -58,7 +58,7 @@ struct CodableMessage: Codable { let fromPrior = try? container.decodeIfPresent(String.self, forKey: .fromPrior) let thid = try? container.decodeIfPresent(String.self, forKey: .thid) let pthid = try? container.decodeIfPresent(String.self, forKey: .pthid) - let directionRaw = try container.decodeIfPresent(String.self, forKey: .direction) + let directionRaw = try container.decodeIfPresent(Int.self, forKey: .direction) let direction = directionRaw.flatMap { Message.Direction(rawValue: $0) } self.init(message: .init( diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDCredentialDAO+CredentialStore.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDCredentialDAO+CredentialStore.swift index 77794ab9..ed772ddb 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDCredentialDAO+CredentialStore.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDCredentialDAO+CredentialStore.swift @@ -23,8 +23,25 @@ extension CDCredentialDAO: CredentialStore { } func addCredentials(credentials: [StorableCredential]) -> AnyPublisher { - credentials.publisher.flatMap { - self.addCredential(credential: $0) + batchUpdateOrCreate( + credentials.map(\.storingId), + context: writeContext + ) { id, cdobj, context in + guard let credential = credentials.first(where: { $0.storingId == id}) else { + throw PlutoError.unknownCredentialTypeError + } + let claimsObjs = credential.queryAvailableClaims.map { + let obj = CDAvailableClaim( + entity: CDAvailableClaim.entity(), + insertInto: context + ) + obj.value = $0 + return obj + } + try cdobj.parseFromDomain( + from: credential, + withClaims: Set(claimsObjs) + ) } .map { _ in } .eraseToAnyPublisher() diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift index 3258730f..993e5452 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift @@ -23,7 +23,7 @@ extension CDMessageDAO: MessageProvider { func getAllSent() -> AnyPublisher<[Message], Error> { fetchController( - predicate: NSPredicate(format: "(direction == %@)", "sent"), + predicate: NSPredicate(format: "(direction == %d)", 0), context: readContext ) .tryMap { try $0.map { try $0.toDomain() } } @@ -32,7 +32,7 @@ extension CDMessageDAO: MessageProvider { func getAllReceived() -> AnyPublisher<[Message], Error> { fetchController( - predicate: NSPredicate(format: "(direction == %@)", "received"), + predicate: NSPredicate(format: "(direction == %d)", 1), context: readContext ) .tryMap { try $0.map { try $0.toDomain() } } diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageStore.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageStore.swift index 1e4129e1..8497c4b0 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageStore.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageStore.swift @@ -6,10 +6,25 @@ extension CDMessageDAO: MessageStore { func addMessages(messages: [(Message, Message.Direction)]) -> AnyPublisher { messages .publisher - .flatMap { - self.addMessage(msg: $0.0, direction: $0.1) + .flatMap { (message, direction) in + self.fetchDIDPair(from: message.from, to: message.to) + .map { + (message, direction, $0) + } } .collect() + .eraseToAnyPublisher() + .flatMap { messages in + self.batchUpdateOrCreate( + messages.map(\.0.id), + context: writeContext + ) { id, cdobj, _ in + guard let domainObjs = messages.first(where: { $0.0.id == id }) else { + return + } + try cdobj.fromDomain(msg: domainObjs.0, direction: domainObjs.1, pair: domainObjs.2) + } + } .map { _ in () } .eraseToAnyPublisher() } @@ -37,6 +52,10 @@ extension CDMessageDAO: MessageStore { } } .map { _ in } + .mapError { + print($0) + return $0 + } .eraseToAnyPublisher() } @@ -47,6 +66,23 @@ extension CDMessageDAO: MessageStore { func removeAll() -> AnyPublisher { deleteAllPublisher(context: writeContext) } + + private func fetchDIDPair(from: DID?, to: DID?) -> AnyPublisher { + pairDAO + .fetchController( + predicate: NSPredicate( + format: "(holderDID.did == %@) OR (holderDID.did == %@) OR (did == %@) OR (did == %@)", + from?.string ?? "", + to?.string ?? "", + from?.string ?? "", + to?.string ?? "" + ), + context: writeContext + ) + .first() + .map { $0.first } + .eraseToAnyPublisher() + } } private extension CDMessage { diff --git a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDMessage+CoreDataProperties.swift b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDMessage+CoreDataProperties.swift index e5e2bd63..08bfc087 100644 --- a/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDMessage+CoreDataProperties.swift +++ b/EdgeAgentSDK/Pluto/Sources/PersistentStorage/Models/CDMessage+CoreDataProperties.swift @@ -13,7 +13,7 @@ extension CDMessage { @NSManaged var from: String? @NSManaged var to: String? @NSManaged var thid: String? - @NSManaged var direction: String? + @NSManaged var direction: Int @NSManaged var pair: CDDIDPair? } diff --git a/EdgeAgentSDK/Pluto/Sources/PlutoImpl+Public.swift b/EdgeAgentSDK/Pluto/Sources/PlutoImpl+Public.swift index 1c67b254..3ee2955a 100644 --- a/EdgeAgentSDK/Pluto/Sources/PlutoImpl+Public.swift +++ b/EdgeAgentSDK/Pluto/Sources/PlutoImpl+Public.swift @@ -50,6 +50,10 @@ extension PlutoImpl: Pluto { mediatorDAO.addMediator(peer: peer, routingDID: routingDID, mediatorDID: mediatorDID) } + public func storeCredentials(credentials: [StorableCredential]) -> AnyPublisher { + credentialsDAO.addCredentials(credentials: credentials) + } + public func storeCredential(credential: StorableCredential) -> AnyPublisher { credentialsDAO.addCredential(credential: credential) } diff --git a/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents index 779cf85c..74614fac 100644 --- a/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents +++ b/EdgeAgentSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -58,7 +58,7 @@ - + diff --git a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialImporter.swift b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialImporter.swift index a4738f25..f5f111c3 100644 --- a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialImporter.swift +++ b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialImporter.swift @@ -46,11 +46,14 @@ private func importAnoncredCredential( let domainCred = try JSONDecoder().decode(AnonCredential.self, from: credentialData) let credentialDefinitionData = try? await credentialDefinitionDownloader .downloadFromEndpoint(urlOrDID: domainCred.credentialDefinitionId) - let schemaData = try? await schemaDownloader - .downloadFromEndpoint(urlOrDID: domainCred.schemaId) + let cdData = try credentialDefinitionData.map { try JSONDecoder.didComm().decode(AnonCredentialDefinition.self, from: $0) } + let schemaData = (try? await schemaDownloader + .downloadFromEndpoint(urlOrDID: domainCred.schemaId)) + .flatMap { try? JSONDecoder.didComm().decode(AnonCredentialSchema.self, from: $0) } + return AnoncredsCredentialStack( - schema: schemaData.flatMap { try? JSONDecoder.didComm().decode(AnonCredentialSchema.self, from: $0) }, - definition: try credentialDefinitionData.map { try JSONDecoder.didComm().decode(AnonCredentialDefinition.self, from: $0) }, + schema: schemaData, + definition: cdData, credential: domainCred ) } diff --git a/EdgeAgentSDK/Pollux/Tests/Mocks/MockPluto.swift b/EdgeAgentSDK/Pollux/Tests/Mocks/MockPluto.swift index a3ba38dd..266cb92e 100644 --- a/EdgeAgentSDK/Pollux/Tests/Mocks/MockPluto.swift +++ b/EdgeAgentSDK/Pollux/Tests/Mocks/MockPluto.swift @@ -32,7 +32,12 @@ class MockPluto: Pluto { func storeMediator(peer: Domain.DID, routingDID: Domain.DID, mediatorDID: Domain.DID) -> AnyPublisher { Just(()).tryMap { $0 }.eraseToAnyPublisher() } - + + func storeCredentials(credentials: [any StorableCredential]) -> AnyPublisher { + self.credentials.append(contentsOf: credentials) + return Just(()).tryMap { $0 }.eraseToAnyPublisher() + } + func storeCredential(credential: Domain.StorableCredential) -> AnyPublisher { credentials.append(credential) return Just(()).tryMap { $0 }.eraseToAnyPublisher() diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj index b4b8076d..f9fe724d 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj @@ -127,6 +127,9 @@ EEE620182937F6870053AE52 /* SigningVerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE620172937F6870053AE52 /* SigningVerificationView.swift */; }; EEE6202F2937FDE50053AE52 /* AuthenticateWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE6202D2937FDE50053AE52 /* AuthenticateWalletView.swift */; }; EEE620302937FDE50053AE52 /* AuthenticateWalletViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE6202E2937FDE50053AE52 /* AuthenticateWalletViewModel.swift */; }; + EEE80CEE2C19B5A2000CB94B /* BackupViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE80CED2C19B5A2000CB94B /* BackupViewBuilder.swift */; }; + EEE80CF02C19B5AF000CB94B /* BackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE80CEF2C19B5AF000CB94B /* BackupViewModel.swift */; }; + EEE80CF22C19B5C3000CB94B /* BackupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE80CF12C19B5C3000CB94B /* BackupView.swift */; }; EEECB27F29C282A800BBB4B9 /* ConnectionsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEECB27E29C282A800BBB4B9 /* ConnectionsListView.swift */; }; EEECB28129C282D500BBB4B9 /* ConnectionsViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEECB28029C282D500BBB4B9 /* ConnectionsViewState.swift */; }; EEECB28329C2831E00BBB4B9 /* ConnectionsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEECB28229C2831E00BBB4B9 /* ConnectionsListViewModel.swift */; }; @@ -256,6 +259,9 @@ EEE6202D2937FDE50053AE52 /* AuthenticateWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticateWalletView.swift; sourceTree = ""; }; EEE6202E2937FDE50053AE52 /* AuthenticateWalletViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticateWalletViewModel.swift; sourceTree = ""; }; EEE620312937FF2C0053AE52 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + EEE80CED2C19B5A2000CB94B /* BackupViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupViewBuilder.swift; sourceTree = ""; }; + EEE80CEF2C19B5AF000CB94B /* BackupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupViewModel.swift; sourceTree = ""; }; + EEE80CF12C19B5C3000CB94B /* BackupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupView.swift; sourceTree = ""; }; EEECB27E29C282A800BBB4B9 /* ConnectionsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsListView.swift; sourceTree = ""; }; EEECB28029C282D500BBB4B9 /* ConnectionsViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsViewState.swift; sourceTree = ""; }; EEECB28229C2831E00BBB4B9 /* ConnectionsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsListViewModel.swift; sourceTree = ""; }; @@ -391,6 +397,7 @@ EE6C7F4729C234C200D866AD /* WalletDemo2 */ = { isa = PBXGroup; children = ( + EEE80CEA2C19B582000CB94B /* Backup */, EE8F37502B87738F00EC0638 /* Settings */, EE92C4422B7E2B9D00FC0B6E /* Common */, EE92C4322B7E1F2E00FC0B6E /* AddNewContact */, @@ -756,6 +763,16 @@ path = AuthenticateWallet; sourceTree = ""; }; + EEE80CEA2C19B582000CB94B /* Backup */ = { + isa = PBXGroup; + children = ( + EEE80CED2C19B5A2000CB94B /* BackupViewBuilder.swift */, + EEE80CEF2C19B5AF000CB94B /* BackupViewModel.swift */, + EEE80CF12C19B5C3000CB94B /* BackupView.swift */, + ); + path = Backup; + sourceTree = ""; + }; EEECB27D29C2825400BBB4B9 /* Connections */ = { isa = PBXGroup; children = ( @@ -918,11 +935,13 @@ EEE61FE02937CEAA0053AE52 /* SeedViewModel.swift in Sources */, EE92C41E2B7E1CA200FC0B6E /* WebView.swift in Sources */, EEBC939529C735910015A36E /* CredentialListViewModel.swift in Sources */, + EEE80CF02C19B5AF000CB94B /* BackupViewModel.swift in Sources */, EE6C38DC294626E1006CD2D3 /* String+extensions.swift in Sources */, EE92C40A2B7E1CA200FC0B6E /* NavigationBarUtilModifiers.swift in Sources */, EE92C4262B7E1CA200FC0B6E /* SearchBoxView.swift in Sources */, EE92C4462B7E2D8500FC0B6E /* ConnectionsListRouter.swift in Sources */, EE92C41A2B7E1CA200FC0B6E /* ButtonNavigationLink.swift in Sources */, + EEE80CF22C19B5C3000CB94B /* BackupView.swift in Sources */, EE92C42A2B7E1CA200FC0B6E /* IntrinsicSizePreferenceKey.swift in Sources */, EE92C40D2B7E1CA200FC0B6E /* AtalaNavigationBackButton.swift in Sources */, EE92C4172B7E1CA200FC0B6E /* FlowSuccessfulView.swift in Sources */, @@ -934,6 +953,7 @@ EE549F492ACC1F7D0038ED1D /* CredentialDetailViewState.swift in Sources */, EE92C43E2B7E1F6A00FC0B6E /* ConfirmConnectionView.swift in Sources */, EE418AF32BCFD926008766A6 /* MainVerifierRouter.swift in Sources */, + EEE80CEE2C19B5A2000CB94B /* BackupViewBuilder.swift in Sources */, EE8F37522B87739D00EC0638 /* SettingsView.swift in Sources */, EE92C3D12B7E1C0D00FC0B6E /* QRCodeScannerBuilder.swift in Sources */, EE92C3D02B7E1C0D00FC0B6E /* CameraViewController.swift in Sources */, diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupView.swift new file mode 100644 index 00000000..bc77c3a4 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupView.swift @@ -0,0 +1,51 @@ +import SwiftUI + +protocol BackupViewModel: ObservableObject { + var newJWE: String? { get } + + func createNewJWE() async throws + func backupWith(_ jwe: String) async throws +} + +struct BackupView: View { + @StateObject var viewModel: ViewModel + @State private var jwe: String = "" + var body: some View { + VStack(spacing: 10) { + VStack(spacing: 8) { + AtalaButton( + configuration: .primary, + action: { + Task { + try await self.viewModel.createNewJWE() + } + }, + label: { + Text("Create Backup".localize()) + } + ) + if let jwe = viewModel.newJWE { + Text(jwe) + .font(.caption) + .textSelection(.enabled) + } + } + Divider() + VStack(spacing: 8) { + TextField("Insert backup here", text: $jwe) + AtalaButton( + configuration: .primary, + action: { + Task { + try await self.viewModel.backupWith(jwe) + } + }, + label: { + Text("Backup".localize()) + } + ) + } + } + .padding(15) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewBuilder.swift new file mode 100644 index 00000000..16286dae --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewBuilder.swift @@ -0,0 +1,15 @@ +import EdgeAgent +import SwiftUI + +struct BackupComponent: ComponentContainer { + let container: DIContainer +} + +struct BackupBuilder: Builder { + func build(component: BackupComponent) -> some View { + let viewModel = getViewModel(component: component) { + BackupViewModelImpl(agent: component.container.resolve(type: EdgeAgent.self)!) + } + return BackupView(viewModel: viewModel) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewModel.swift new file mode 100644 index 00000000..b35c250d --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Backup/BackupViewModel.swift @@ -0,0 +1,25 @@ +import Domain +import EdgeAgent +import Foundation + +final class BackupViewModelImpl: BackupViewModel { + @Published var newJWE: String? = nil + + private let agent: EdgeAgent + + init(agent: EdgeAgent) { + self.agent = agent + } + + func createNewJWE() async throws { + let jwe = try await agent.backupWallet() + + await MainActor.run { + self.newJWE = jwe + } + } + + func backupWith(_ jwe: String) async throws { + try await agent.recoverWallet(encrypted: jwe) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift index 0f79466c..b110bbe4 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift @@ -21,9 +21,13 @@ final class Main2RouterImpl: Main2ViewRouter { ) ).build() - let mnemonics = ["pig", "fork", "educate", "gun", "entire", "scatter", "satoshi", "laugh", "project", "buffalo", "race", "enroll", "shiver", "theme", "similar", "thought", "prepare", "velvet", "wild", "mention", "jelly", "match", "document", "rapid"] +// let mnemonics = ["pig", "fork", "educate", "gun", "entire", "scatter", "satoshi", "laugh", "project", "buffalo", "race", "enroll", "shiver", "theme", "similar", "thought", "prepare", "velvet", "wild", "mention", "jelly", "match", "document", "rapid"] +// +// let seed = try! apollo.createSeed(mnemonics: mnemonics, passphrase: "") - let seed = try! apollo.createSeed(mnemonics: mnemonics, passphrase: "") + let byteArray: [UInt8] = [69, 191, 35, 232, 213, 102, 3, 93, 180, 106, 224, 144, 79, 171, 79, 223, 154, 217, 235, 232, 96, 30, 248, 92, 100, 38, 38, 42, 101, 53, 2, 247, 56, 111, 148, 220, 237, 122, 15, 120, 55, 82, 89, 150, 35, 45, 123, 135, 159, 140, 52, 127, 239, 148, 150, 109, 86, 145, 77, 109, 47, 60, 20, 16] + + let seed = Seed(value: Data(byteArray)) let agent = EdgeAgent( apollo: apollo, diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsView.swift index 63393ff3..5c8d9d12 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsView.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsView.swift @@ -7,15 +7,18 @@ protocol SettingsViewModel: ObservableObject { protocol SettingsViewRouter { associatedtype MediatorV: View associatedtype DIDsV: View + associatedtype BackupV: View func routeToMediator() -> MediatorV func routeToDIDs() -> DIDsV + func routeToBackup() -> BackupV } class SettingsViewModelImpl: SettingsViewModel { @Published var menu = [ SettingsViewState.Menu.mediator, - SettingsViewState.Menu.dids + SettingsViewState.Menu.dids, + SettingsViewState.Menu.backup ] } @@ -40,6 +43,8 @@ struct SettingsView: V router.routeToDIDs() case .mediator: router.routeToMediator() + case .backup: + router.routeToBackup() } } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewRouter.swift index b4f0934d..b892e7bb 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewRouter.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewRouter.swift @@ -22,4 +22,10 @@ struct SettingsViewRouterImpl: SettingsViewRouter { ) return MediatorPageView(viewModel: viewModel) } + + func routeToBackup() -> some View { + BackupBuilder().build(component: .init( + container: container + )) + } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewState.swift index 84b6514b..58d1f42e 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewState.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Settings/SettingsViewState.swift @@ -4,6 +4,7 @@ struct SettingsViewState { enum Menu: String, Identifiable { case dids = "DIDs" case mediator = "Mediator" + case backup = "Backup" var id: String { self.rawValue