From 33b24a70a72ee3478ffde1a27f615e51e3a07750 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Thu, 9 Feb 2023 13:23:28 +0100 Subject: [PATCH] Feat: encrypted mqtt sending --- crypto_helper/.gitignore | 5 + crypto_helper/Cargo.lock | 534 ++++++++++++++++++++++++++++++++++++ crypto_helper/Cargo.toml | 38 +++ crypto_helper/README.md | 86 ++++++ crypto_helper/src/crypto.rs | 60 ++++ crypto_helper/src/lib.rs | 20 ++ crypto_helper/src/utils.rs | 10 + crypto_helper/tests/web.rs | 13 + index.css | 31 ++- index.html | 61 +++- js/index.js | 154 ++++------- js/localState.js | 105 +++++++ js/mqtt.js | 117 ++++++++ lib/crypto_helper.js | 280 +++++++++++++++++++ lib/crypto_helper_bg.wasm | Bin 0 -> 103096 bytes 15 files changed, 1396 insertions(+), 118 deletions(-) create mode 100644 crypto_helper/.gitignore create mode 100644 crypto_helper/Cargo.lock create mode 100644 crypto_helper/Cargo.toml create mode 100644 crypto_helper/README.md create mode 100644 crypto_helper/src/crypto.rs create mode 100644 crypto_helper/src/lib.rs create mode 100644 crypto_helper/src/utils.rs create mode 100644 crypto_helper/tests/web.rs create mode 100644 js/localState.js create mode 100644 js/mqtt.js create mode 100644 lib/crypto_helper.js create mode 100644 lib/crypto_helper_bg.wasm diff --git a/crypto_helper/.gitignore b/crypto_helper/.gitignore new file mode 100644 index 0000000..1711838 --- /dev/null +++ b/crypto_helper/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +bin/ +pkg/ +wasm-pack.log diff --git a/crypto_helper/Cargo.lock b/crypto_helper/Cargo.lock new file mode 100644 index 0000000..a21755a --- /dev/null +++ b/crypto_helper/Cargo.lock @@ -0,0 +1,534 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto_helper" +version = "0.1.0" +dependencies = [ + "aes-gcm", + "base64", + "console_error_panic_hook", + "generic-array", + "hex", + "pbkdf2", + "sha256", + "wasm-bindgen", + "wasm-bindgen-test", + "wee_alloc", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", + "hmac", + "password-hash", + "sha2 0.10.6", +] + +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha256" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +dependencies = [ + "hex", + "sha2 0.9.9", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db36fc0f9fb209e88fb3642590ae0205bb5a56216dabd963ba15879fe53a30b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0734759ae6b3b1717d661fe4f016efcfb9828f5edb4520c18eaee05af3b43be9" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/crypto_helper/Cargo.toml b/crypto_helper/Cargo.toml new file mode 100644 index 0000000..b811977 --- /dev/null +++ b/crypto_helper/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "crypto_helper" +version = "0.1.0" +authors = ["Dorian Zedler "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.63" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +wee_alloc = { version = "0.4.5", optional = true } +base64 = "0.21.0" +pbkdf2 = "0.11.0" +hex = "0.4.3" +aes-gcm = { version="0.10.1", features = ["aes", "alloc"], default-features = false } +generic-array = "0.14.6" +sha256 = "1.1.1" + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/crypto_helper/README.md b/crypto_helper/README.md new file mode 100644 index 0000000..be29ebd --- /dev/null +++ b/crypto_helper/README.md @@ -0,0 +1,86 @@ +
+ +

wasm-pack-template

+ + A template for kick starting a Rust and WebAssembly project using wasm-pack. + +

+ Build Status +

+ +

+ Tutorial + | + Chat +

+ + Built with 🦀🕸 by The Rust and WebAssembly Working Group +
+ +## About + +[**📚 Read this template tutorial! 📚**][template-docs] + +This template is designed for compiling Rust libraries into WebAssembly and +publishing the resulting package to NPM. + +Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other +templates and usages of `wasm-pack`. + +[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html +[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html + +## 🚴 Usage + +### 🐑 Use `cargo generate` to Clone this Template + +[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) + +``` +cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project +cd my-project +``` + +### 🛠️ Build with `wasm-pack build` + +``` +wasm-pack build +``` + +### 🔬 Test in Headless Browsers with `wasm-pack test` + +``` +wasm-pack test --headless --firefox +``` + +### 🎁 Publish to NPM with `wasm-pack publish` + +``` +wasm-pack publish +``` + +## 🔋 Batteries Included + +* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating + between WebAssembly and JavaScript. +* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) + for logging panic messages to the developer console. +* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized + for small code size. +* `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. \ No newline at end of file diff --git a/crypto_helper/src/crypto.rs b/crypto_helper/src/crypto.rs new file mode 100644 index 0000000..7b24c41 --- /dev/null +++ b/crypto_helper/src/crypto.rs @@ -0,0 +1,60 @@ +use pbkdf2::{self, password_hash::PasswordHasher}; +use base64::{Engine as _}; +use generic_array::typenum::{UInt, B1, B0, UTerm, U32}; + +use wasm_bindgen::prelude::*; + +use aes_gcm::{ + aead::{Aead, KeyInit, generic_array::GenericArray}, + Aes256Gcm +}; + +#[wasm_bindgen] +pub struct Crypto { + iv: IV, + c: Aes256Gcm +} + +type Key = GenericArray; +type IV = GenericArray, B1>, B0>, B0>>; + +#[wasm_bindgen] +impl Crypto { + #[wasm_bindgen(constructor)] + pub fn new(password: &str, salt: &str) -> Self { + let key = Self::_pbkdf2(password, salt, 1000, 32); + let iv = Self::_pbkdf2(password, salt, 5000, 12); + + println!("Key {}\nIV {}", hex::encode(&key), hex::encode(&iv)); + + + let iv: IV = GenericArray::clone_from_slice(&iv); + let key: &Key = GenericArray::from_slice(&key); + let c = Aes256Gcm::new(key); + + Crypto { iv: iv, c:c } + } + + pub fn sha256(input: &str) -> String { + sha256::digest(input) + } + + fn _pbkdf2(password: &str, salt: &str, rounds: u32, output_length: usize) -> Vec { + let params = pbkdf2::Params { rounds: rounds, output_length: output_length }; + let salt = base64::engine::general_purpose::STANDARD_NO_PAD.encode(salt); + let salt = pbkdf2::password_hash::Salt::new(&salt).unwrap(); + + let res = pbkdf2::Pbkdf2.hash_password_customized(password.as_bytes(), None, None, params, salt).unwrap(); + + res.hash.unwrap().as_bytes().to_owned() + } + + pub fn encrypt(&self, plaintext: &str) -> String { + base64::engine::general_purpose::STANDARD_NO_PAD.encode(self.c.encrypt(&self.iv, plaintext.as_bytes()).unwrap()) + } + + pub fn decrypt(&self, ciphertext: &str) -> String { + let ciphertext = base64::engine::general_purpose::STANDARD_NO_PAD.decode(ciphertext).unwrap(); + String::from_utf8(self.c.decrypt(&self.iv, &*ciphertext).unwrap()).unwrap() + } +} \ No newline at end of file diff --git a/crypto_helper/src/lib.rs b/crypto_helper/src/lib.rs new file mode 100644 index 0000000..d5df469 --- /dev/null +++ b/crypto_helper/src/lib.rs @@ -0,0 +1,20 @@ +mod utils; +pub mod crypto; + +use wasm_bindgen::prelude::*; + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[wasm_bindgen] +extern { + fn alert(s: &str); +} + +#[wasm_bindgen] +pub fn greet() { + alert("Hello, crypto!"); +} diff --git a/crypto_helper/src/utils.rs b/crypto_helper/src/utils.rs new file mode 100644 index 0000000..b1d7929 --- /dev/null +++ b/crypto_helper/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/crypto_helper/tests/web.rs b/crypto_helper/tests/web.rs new file mode 100644 index 0000000..de5c1da --- /dev/null +++ b/crypto_helper/tests/web.rs @@ -0,0 +1,13 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1 + 1, 2); +} diff --git a/index.css b/index.css index 55b1bfb..bdc80aa 100644 --- a/index.css +++ b/index.css @@ -4,6 +4,13 @@ } } +.loading { + font-size: 2em; + font-weight: bold; + text-align: center; + line-height: 1.2; +} + .timer { font-size: 8em; font-weight: bold; @@ -16,13 +23,31 @@ animation: blinker 2s ease infinite; } +.timer.sending { + color: #ff0; + animation: blinker 2s ease infinite; +} + main { - height: 100vh; + padding-top: 0 !important; } -main > div { - height: 100%; +.timer-container-div { + height: 100vh; display: flex; align-items: center; justify-content: center; +} + +.timer-div { + cursor: pointer; +} + +.tap-area { + width: 100vw; + height: 100vh; + position: absolute; + top: 0; + left: 0; + cursor: pointer; } \ No newline at end of file diff --git a/index.html b/index.html index 1fde5e4..95a841c 100644 --- a/index.html +++ b/index.html @@ -6,13 +6,15 @@ - - + + - + + + - + @@ -28,21 +30,50 @@ -
-
-
- - -
-

OK! .. READY! ... GO!

-

-
+ +
+
+
+
+
+

OK! .. READY! ... GO!

+

+
-

+

+ +
+
+ Remote connection + +
+

To send the times to a computer, please enter a password here:

+
+ + + Make sure, this is exactly the same on your computer! + +
+
+ +
+

Connected

+ +
+
+
+ +
+

Connecting...

+
\ No newline at end of file diff --git a/js/index.js b/js/index.js index 78bd920..06e5afa 100644 --- a/js/index.js +++ b/js/index.js @@ -1,108 +1,62 @@ -async function play() { - const number = document.getElementById("number").value; - sayNumber(number); -} - async function sayNumber(number) { - console.log(number) - number = number.toString(); - number = number.replace(/[^0-9]/, ""); - for(let i = 0; i < number.length; i++) { - await playAudio(document.getElementById(`sound-${number[i]}`)); - } + console.log(number); + number = number.toString(); + number = number.replace(/[^0-9]/, ""); + for (let i = 0; i < number.length; i++) { + await playAudio(document.getElementById(`sound-${number[i]}`)); + } } -function playAudio(audio){ - return new Promise(res=>{ - audio.play() - audio.onended = res - }) +function playAudio(audio) { + return new Promise((res) => { + audio.play(); + audio.onended = res; + }); +} + +function uuidv4() { + return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => + ( + c ^ + (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4))) + ).toString(16) + ); +} + +function PasswordForm() { + return { + formData: { + password: "", + }, + submitForm() { + Alpine.store("localState").password = this.formData.password; + }, + }; } function Timer() { - return { - time: 0, - over: false, - init() { - setInterval(() => { - const startedAt = Alpine.store("localState").startedAt; - const resultTime = Alpine.store("localState").time; + return { + time: 0, + over: false, + init() { + setInterval(() => { + const startedAt = Alpine.store("localState").startedAt; + const resultTime = Alpine.store("localState").time; - let time; - if (!startedAt && !resultTime) { - time = 0; - this.over = false; - } else if (resultTime) { - time = (resultTime / 1000); - this.over = true; - } else { - this.over = false; - time = - ((new Date().getTime() - startedAt) / 1000) - ; - } + let time; + if (!startedAt && !resultTime) { + time = 0; + this.over = false; + } else if (resultTime) { + time = resultTime / 1000; + this.over = true; + } else { + this.over = false; + time = (new Date().getTime() - startedAt) / 1000; + } - this.time = time.toFixed(2) - }, 10); - }, - }; -} - -document.addEventListener("alpine:init", () => { - - Alpine.store("localState", { - _state: 0, - // 0: idle - // 1: starting - // 2: running - // 3: stopped - - startedAt: null, - time: null, - stateHint: "", - - init() { - Alpine.effect(() => { - switch(this._state) { - case 0: { - this.startedAt = null; - this.time = null; - this.stateHint = "Tap to start"; - break; - } - case 1: { - this.stateHint = "Get ready..." - playAudio(document.getElementById("sound-ok-ready-go")).then(() => { - Alpine.store("localState")._state = 2; - }); - break; - } - case 2: { - this.stateHint = "Tap to stop" - this.startedAt = new Date().getTime() - 200; - break; - } - case 3: { - this.stateHint = "Tap to reset" - this.time = new Date().getTime() - this.startedAt; - sayNumber((this.time / 1000).toFixed(2)); - this.startedAt = null; - break; - } - } - }) - }, - - next() { - console.log("next"); - playAudio(document.getElementById("sound-silence")) - - if(this._state == 1) return; - - this._state = (this._state + 1) % 4 - }, - }) - - - -}) \ No newline at end of file + this.time = time.toFixed(2); + }, 10); + }, + }; +} \ No newline at end of file diff --git a/js/localState.js b/js/localState.js new file mode 100644 index 0000000..18513c2 --- /dev/null +++ b/js/localState.js @@ -0,0 +1,105 @@ +document.addEventListener("alpine:init", () => { + Alpine.store("localState", { + _state: 0, + // 0: idle + // 1: starting + // 2: running + // 3: sending time to mqtt + // 4: stopped + // 5: connecting to mqtt + + startedAt: null, + time: null, + stateHint: "", + password: null, + + init() { + Alpine.effect(() => { + if (this.password == null) { + this.password = localStorage.getItem("password"); + } else { + localStorage.setItem("password", this.password); + } + + const mqtt = Alpine.store("mqtt"); + if (!mqtt) return; + if (this.password == null || this.password == "") { + mqtt.disconnect(); + } else { + mqtt.connect(); + } + }); + + Alpine.effect(() => { + switch (this._state) { + case 0: + this.stateHint = "Tap here to start"; + break; + case 1: + this.stateHint = "Get ready..."; + break; + case 2: + this.stateHint = "Tap anywhere to stop"; + break; + case 3: + this.stateHint = "Sending time to MQTT..."; + break; + + case 4: + this.stateHint = "Tap here to reset"; + break; + } + }); + }, + + next() { + playAudio(document.getElementById("sound-silence")); + + if (this._state == 1 || this._state == 3) return; + + this._setState((this._state + 1) % 5); + }, + + _setState(state) { + switch (state) { + case 0: { + this.startedAt = null; + this.time = null; + break; + } + case 1: { + playAudio(document.getElementById("sound-ok-ready-go")).then(() => { + Alpine.store("localState")._setState(2); + }); + break; + } + case 2: { + this.startedAt = new Date().getTime() - 200; + break; + } + case 3: { + this.time = + ((new Date().getTime() - this.startedAt) / 1000).toFixed(2) * 1000; + this.startedAt = null; + sayNumber(this.time / 10); + + if (Alpine.store("mqtt").connected) { + Alpine.store("mqtt") + .sendTime(this.time) + .then(() => { + Alpine.store("localState")._setState(4); + }); + break; + } + + state = 4; + break; + } + default: + break; + } + + this._state = state; + }, + }); +}); diff --git a/js/mqtt.js b/js/mqtt.js new file mode 100644 index 0000000..0c0b6ea --- /dev/null +++ b/js/mqtt.js @@ -0,0 +1,117 @@ +let wasm_inited_resolve; +const wasm_inited = new Promise((resolve) => {wasm_inited_resolve = resolve;}); + +document.addEventListener("wasm-loaded", () => { + wasm_inited_resolve(wasm_bindgen) +}); + +document.addEventListener("alpine:init", () => { + Alpine.store("mqtt", { + connected: false, + _client: null, + _topic: null, + _c: null, + + _pendingPromises: {}, + + sendTime(time) { + if (!this.connected) return null; + + const id = uuidv4(); + const promise = new Promise((resolve, reject) => {this._pendingPromises[id] = [resolve, reject]}); + this._publish({ + id: id, // used to prevent replay attacks and to identify confirm messages + kind: "Time", // can be "time" or "confirm" + time: time, // only used for "time" + }); + + return promise; + }, + + async connect() { + if (this.connected) return; + + const password = Alpine.store("localState").password; + const that = this; + const brokerDomain = "broker.emqx.io"; + const url = `wss://${brokerDomain}:8084/mqtt`; + + if (!password) return false; + + const {Crypto} = await wasm_inited; + + Alpine.store("localState")._state = 5; + + // derive key from password + this._c = new Crypto(password, brokerDomain); + + console.log("Test", this._encrypt("test")); + + this._topic = Crypto.sha256( + this._encrypt(`org.speedclimbing.ok-ready-go.${password}`) + ).toString(); + + console.log("Connecting to MQTT broker..."); + console.log("topic:", this._topic); + + const options = { + // Clean session + clean: true, + connectTimeout: 4000, + }; + + this._client = mqtt.connect(url, options); + + this._client.on("connect", () => { + Alpine.store("localState")._state = 0; + + that._client.subscribe(that._topic); + this.connected = true; + }); + + this._client.on("message", (topic, message) => { + // message is Buffer + message = that._decrypt(message.toString()); + const data = JSON.parse(message); + + if (topic !== that._topic || data.kind !== "Confirm" || Object.keys(this._pendingPromises).indexOf(data.id) === -1) return; + + console.log("<<< ", data); + this._pendingPromises[data.id][0](); + }); + }, + + disconnect() { + if(!this.connected) return; + + this._client.end(true); + + this._client = null; + this.connected = false; + this._topic = null; + + for (const promiseId in this._pendingPromises) { + this._pendingPromises[promiseId][1](); + } + + this._pendingPromises = {}; + }, + + _publish(data) { + const encryptedData = this._encrypt(JSON.stringify(data)); + console.log(">>> ", data); + this._client.publish(this._topic, encryptedData, { + qos: 1, + retain: false, + }); + }, + + _encrypt(data) { + return this._c.encrypt(data); + }, + + _decrypt(data) { + return this._c.decrypt(data); + }, + }); +}); diff --git a/lib/crypto_helper.js b/lib/crypto_helper.js new file mode 100644 index 0000000..789d119 --- /dev/null +++ b/lib/crypto_helper.js @@ -0,0 +1,280 @@ +let wasm_bindgen; +(function() { + const __exports = {}; + let script_src; + if (typeof document === 'undefined') { + script_src = location.href; + } else { + script_src = new URL(document.currentScript.src, location.href).toString(); + } + let wasm; + + const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + + cachedTextDecoder.decode(); + + let cachedUint8Memory0 = null; + + function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; + } + + function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); + } + + let WASM_VECTOR_LEN = 0; + + const cachedTextEncoder = new TextEncoder('utf-8'); + + const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); + } + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }); + + function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length); + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len); + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3); + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; + } + + let cachedInt32Memory0 = null; + + function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; + } + /** + */ + __exports.greet = function() { + wasm.greet(); + }; + + /** + */ + class Crypto { + + static __wrap(ptr) { + const obj = Object.create(Crypto.prototype); + obj.ptr = ptr; + + return obj; + } + + __destroy_into_raw() { + const ptr = this.ptr; + this.ptr = 0; + + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_crypto_free(ptr); + } + /** + * @param {string} password + * @param {string} salt + */ + constructor(password, salt) { + const ptr0 = passStringToWasm0(password, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(salt, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.crypto_new(ptr0, len0, ptr1, len1); + return Crypto.__wrap(ret); + } + /** + * @param {string} input + * @returns {string} + */ + static sha256(input) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.crypto_sha256(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(r0, r1); + } + } + /** + * @param {string} plaintext + * @returns {string} + */ + encrypt(plaintext) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(plaintext, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.crypto_encrypt(retptr, this.ptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(r0, r1); + } + } + /** + * @param {string} ciphertext + * @returns {string} + */ + decrypt(ciphertext) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(ciphertext, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.crypto_decrypt(retptr, this.ptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(r0, r1); + } + } + } + __exports.Crypto = Crypto; + + async function load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } + } + + function getImports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg_alert_0cc0cb8b17d72dde = function(arg0, arg1) { + alert(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + + return imports; + } + + function initMemory(imports, maybe_memory) { + + } + + function finalizeInit(instance, module) { + wasm = instance.exports; + init.__wbindgen_wasm_module = module; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + + return wasm; + } + + function initSync(module) { + const imports = getImports(); + + initMemory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return finalizeInit(instance, module); + } + + async function init(input) { + if (typeof input === 'undefined') { + input = script_src.replace(/\.js$/, '_bg.wasm'); + } + const imports = getImports(); + + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); + } + + initMemory(imports); + + const { instance, module } = await load(await input, imports); + + return finalizeInit(instance, module); + } + + wasm_bindgen = Object.assign(init, { initSync }, __exports); + +})(); diff --git a/lib/crypto_helper_bg.wasm b/lib/crypto_helper_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..baf2cecd72a58a2a365439288ebbaa4a90f264de GIT binary patch literal 103096 zcmeFa3!I%rk@x>x&TZzL$(%_73`ro*IR+S3qS3`93#ikx+#<4|>$>dfF1X68J7JZ{ zWCOmN*9;`Ypb?^i20;lJC2}(gY7~^=q6P_qfQpI|1vDy3RMemd|L?E*dCr`sQK?jDF+4qd-Rtsk7<3d`5CJzZFc_4Jw}<7zf1LUsZ(H*ADCqEPdgGF} zpT6{MC;zV{r=A-8K5R`N-+Jm9!CTwXhyJ&`;l(eRAFK|iq%Tf=tK9_umc4k>ss6+wVrbLKi+WKX>WUD@a=GF6Wf=Z>fin{oZa;H4R3nW$zapJ^bK!ZeDdjU z`}emlJ#|TNS=8QC#V|c9YPm8D0~eP6`SR$a(U+qyMR$aM3U7~2-WaWkZjX*x6MigO z9o-eSlx~l{7p{&rQT9;yK=_g9-4tICeInY(n`@)1qicBn_nqM%!nM&0F6P~r!_Cph z!>@#Yb47S|_^Ie);i~A9(GB4_;Y+R#zZxEORrtGbYxLFVr{M`_ej|KO^c6mTApC6f zyYR!|hobYNck=v|=)l)p6MjD05S{pm@C(t)U)B2l&*ryAkH0o5tT^c%&kNknaB)4J z6C|-42&*?lwKxe5ju+OB)@5F8Z(X`-({)K|Q+4UA9ivONwvR5Q+B98)+LR>9bthpG zx#H5^S}E>!K`n~AlPHOM^}6@qAgH(XEL@$m^>nT78LOIPi4TsuYy0W) zmRhGS3v2u9GOspUms4u{>T*(ThAv&TM3&yl5-Ma3zAl{*CWkI~1 zD`@xfceDK+w7+-T-|b; zAWT87n*^j=&!;)@_f?5nsJ&WUT^H8=Ue|%J_8J-l4MPhw46WHITKlJHb*5<1U;3R- z)LsLXbr~ufCPC%kctcpL3+$$__B_A>#=ru`!0wlVh0I27$ZF(%bxQ8%rR1)secvsB zb5ei@01yBM5C8@cJ{o@^y8(Pn3h-4azynf%bM^$_fhoX)00;mB2mk{JUkxAxHGqGg z0;FdUHck#o0Uo#~0J~Cv2h#um3?KjuARIG*kkSA`IwL7PGk^!D0K4`C;9sQxpASF) z7(f6BpxcVL6-?3^0wP&8;E>UP(@O*X{1o_K?FsNhQs8p|wgq6t(ZE2YFj__p1qf>> z&~HOwZi>PodxF9XQWRd86i0&*6%zPqWQW`a7V;WcdTd}{n1X%5o`9W~f_;$(U}Oh? z0fe6h5E2_eNNND-p8E2-gfCL^gmYr2zjS1^AK_ z;Gug0aDEDK0RRDD00CeC;i3VApa$?CQ-Jgg!p6yh6yW?l0k|*)_%}2F00RgB0|>_q zAfz;akj_X-&kW$-qyQK03BbcrfQJJR00s~M031&vK|ljb{|xNmDcHmI1ndzh*q5RX zMj2@XK1wdF&k)mq(^muj(iHd+djkArDe#w%tBc^Fks1;kSV(GM>8pW#c?$MrdjhsQ z1?wO+0F2ZCpto@X4-Fv1HGmM)0Mb7L=u&{)DZoi}5e*su8Z-cK%K$(?0|3Dc06hV~ ze@7Snlh;N6tlpuEUZ*R%==JmppoRoM4GFkrNI*_Q0`eIW^v;kN#q;e_h`Z&o^rXc7 zTfhRwzyij=!Zia6Q4K6aG_e1i68CRY;`ZzbH;+sK9tA)E7(f6RK)7ZAA+iB{eG2fO zQh-OL0FT@gfXAi)7Xc6e1`q%S5H1=(2xA(Q0D$A^A_!<;>7Ri;J_Y;AJpuc7 zDcBSCP!~Z=15RHJ_z5ZSzuObw|B?bfaa>&l4~^82*hmdY4J>^%uqUQq|7B0W{%Z>M zWJnDFBQ*dZb)3LM0|;>qAjC9)^v?jEoC5sU6yT(~hz1P+4H^KrWdIH!Iqz0{-K-h*nHLBEU(v=*GCeY7TS{zYqdkIwkFAP zu&1U-pjOLzJBkDsZTR5ki@$W+uJ1tEWE_U6*(r%NTTW(+Yt)>W1Q%cW$gZ#5xBb%h zLD|`pGpr@qUtFW+#57oc_GjZa|Ktp7P4*Mls5vnaZdtW<{k!h{*~*ia z*Y}&8VdZ3&7)H&BY4D{7Z@%h+?cch2>hk)m$r;v?%oM|@IWY;=KJvcbKm55LuAj5K zK67$@wI(yfFKSLqfgkp<47t`!s zlXI+`RKziAPE3Rg?%eptZPz||r(0gHOwO^EZJS2KQX}<=H^_ZoK88o$r0{ZPw~!3bCBLO09NGMuE>XgYS@W_@=fAOoI-?W;MK&z80#OJBiw#g{)+f`TJ{@eH5@%UV8bxZ>`OdrZ0{ich5^yKe+vy(BboE)TPVc5h(xay{he{=1X7hJlY@wFQqYe^1Nvv6!;BCLD( z?qA$<=dadp^36`hvV*aW#<5D1VXVk7R?LW3%!n`Vx#^Zizk2Qyx1Y6{W_QEqC?{RA zy6Fh3U^3+#j5fFrfB44V|8duYSKmalyWw*@Ur7292HLb*q190?$MT+AF1`F0*FAOi z&ZD8$`bP>z*va@+ zP7V=a8DbLJ@d>;0fzRK)^Xji%Fx}`n*$8`~(G_l$h4lDzee%JrpS|zyTfTRY(RH!` zR!LqUx-!5f7TJ%#@8TbxbHyuj!>+4y>q(G_M*Lf2n>bKSSTeAC4rxRYUv#7?FW z%gHlXpWU{xuXZjaY$k*;HW+-SzP5+kSGv1%G((a+-yC-=a}Cpo(p{r8;_{yNe)31}e9td`^s%1Z z(ukGhP|d<7bviabdC#XeUw^@8nOs8I$r!f4C~ISELdve+_{GnCc;y#vf0Yq-vLV)* z%okzJgiS=)b@zPWsxRMm^S0OSmPY&=qid5w9EYw~4&HXdH#fiMolAF1BUX}yqN}My zv{!1tMwyzPEgyaC!jty5WaHA{CnnYr^UUtV8$ra*Djjm1la2&c`^z9p-yz{3Y-iS@u4SiTiju2fYVIZ@==}1;TpN+HT(`RhtVgtkXY!WEH()>g=z#V z{<*N;f+KV6l{T%~8zEmPO zxk9WYemR%Fkrx|XC)0)}8(k+iU^o8hQ3rKE@RzX^96{N*hLC`79Yb#|-YDz6x2a}xkbWYx(mOHHF4&U;l zzy02~uKVrx9(j1)c~~$kfvd_ur5-YIZM|`#)_l#d~oxJ@44+$ zt1o}lNKJZbE6Y73tF~$Br?Q+}))tnPNMbFs#6*H?m9;A}TRT|CBI~t1IeIxcup}#! zBNiql<&&0^AUS$LypYS@3*vcPrZ0%QxJ+FTCtQwM5O;FfXF*)$GHpRz;xfhZ*xb&r zcTP~g>G1G?73AEmYvkMx25HW1S2U~kyd>y~I%{)unNE%>muiicyP`_%09{J819i#O zYPtkuLuNU*1#)f=8l7|7Mb2&2buLY+uC$cI>nDqSOLV`?kvZGPUFWhTOXf#PoXsh5 zb};aXvt7{+bX=N8F(ogb~yi|OvJ->bA6XLZH21O3ef!RLkoq@28lZ-|J5bu~cKf^2{=)PsOW|G7iM5h0uc@`@a#F2T zm)F-wEf!Wn4y=SKSP5~kQa8#T<5rJxi^sU+F$N7o6zGN@h#OkqJ2^Hvj}1A!!lvM{ zDSC*)28hB2h{6Vl!Ujm9AAm(a;D~;J75(;i-&^&)uf@IfN{ZHg?pGhZ?XG-py)DIW zmfLvFQ}2CyZ+%LN=uG#`FaGNO|J_?}-yMv{r5{)cW3Up&U?q&fYBKtPhS3k`Mn4cY z`hoACfqpaG`fq+@@c#_aZ(n!TXWw%HN+Y9Rr~A^ozj9eLM8AF9t$+N~CMf*>=ocqb z-Q!<);zEXNCVw67sfYfs`phBvRoxZu+4+Me`HS4+KYDEK^5+))_MRWT9{JyEesmI- z>GPx4aG5qgI+4r%ex&T@N6IWeQfB&*GQ*GUef`+(^kaJ;Kenf)Bc&r9Db;kOMCnNB zUOcYyBhRTT>h|jTNUyGs_3HYSrmj(N&q&?u?QXvJ7aP&N8N2s#tIvPpPrS&6%XIgZ z8&*FR3=NlQZriP&-oW^IE)5r0Dt5zCu^X0(-LO>bhNYq{ER|w}rBaNrREiOnN|nP= zsd5-9RSrMR6sGJ(hP>fR*^LyLVw{}aX1R~_mit(5xnC*EEqnHi*uB4d?Ea09!Mlvz z`?)L6z2{!cb;j;l?ty!6c?4xRWL>`d-XDJsh45TrH!KyqVX4>+OT}(jDt5zC(H549 z-LO>bhNWURT0!iFqhdE&LF|T~VmBgWb`Tjdb|XY)2a%%PWVbnSN7gzu){pg0+$-h8 zJ!5vyaQ9sP#LcMcjNSXXU#$J=&A18~yF1<1yVefijAiWJ$9?tbNB+Qz=MuYNsn`um z#co(CcEeJ!8e(?Iyd; zpFgrz_3So({+06QpE0|qy5D?o;By$_jNKjXtCxS|8l0hw-BtIEuY6+u5Vs@ulY2H^ zhI8~>;`U#R=|1Pe2fo;(%lC3yZh3SkFEVaVcTaxrQ$IzyXM=m1d;f>FU-TUFbpK*Z z_kBP9#HPZ~;NH*OaN(xwFw7adXSr+N_wMuX)H8O^bmx3$>-$mv&!y4*7h}3F`^M83 zVyQD-zOTDu+fN6FneLq*xb}T$`;6WDxQ~AHQ#U-v=3##^ru(A1?^xRuw&-vh*FJpx zFw=d`&$nz?F~si3?Hs&v<^QwY=Bc3-e=f26FUEHN@|-ViY>J`o>mK{cg^9|9B2=HcSZtu`e*e`U|DK=F&g&-pc}M=(-oPfO}B3 zfXkZVES6>G%#XBI%OV~}8)*?wOST8hk1AXaoF8fBmLsPkn46OSE@c0)V7IVG+)12G<^Fgio9@R!GNp5j1idN{x4MdCDf*Ec$ zw@*L)bn!SIxa9y7R(aoC4;C+TVIK{JOBcJB^_J^JK)VZe3A4|aB<&Jod1+_*ZZ>L+gL8A^%U(o zi1VWQf#@y65vYDTvfdG#j0rw@?4W# z_2#IS=dEKE+oQgyjb{qhN7_PLeKO3jsuzxG%OwSOW}n;mQ)mE_ZuI!p;zqBEj$PLD zYiYgEcT`)Hge+NC;&KFHZbiSyiQ`0vm$G1fwOTPsa`Kd};Om>NN5?=9i~Cj74}_`` zmJLXfckf7oGy0IMGM!RW)yG39aBEbHDjcfT4F;i`-B%BnR##fjm-dNtqoBqq)NnXd z9wo}4-UK!4UlVK=Tty>YZjRulFo7HOTriJEuUv&j zZ=C>=@!E?m0sUbI%gk=pDI;;UB&2a)yK@xH<>Z=0@iqKnbkfEVL z6CVZM=`gJ`Oipm32{(&Rm`UGaz0;Tq`N&0Lz8_WSHr0~E&@3WC$v~vHdOQ*Z!i&bp zikqY(?^IVK%A^DB+$tzvEap2s~xnpf3X53s6_JAcl|Ci=g(=kVHe(w5bfh-Dx9%j`9c=En#ecKv2CN zQRng*hBAgU0g6D8FO=3V9-mtQZ+M&QO^U~1qRL38H8<2IP12|##!X(z*W)8J!3nHs zRa!H}DFmev(2rKLDb!lj_+CBY+ax+(U#o8s?B?67bBr#=jE2EQQN2CoeSOMe_wUe9 zY9RD-5iU+dRWzZOdCo0dG_zh50~wpi6pf1@v+ymMg~xJwGg&d*)KRtB>f3BxTyK+p zB{?TBfkgk8dP!y@ud%l`Y3p5FCt)WbL9NAwj0G&WZ>^lQU&-2UQzhCWi>;EBU1@P| zuj+4u4ao0+1j?{n?@4noLR&>Im3rMJaGu8nR0uhs5KK1kMyfJ!>vd;pSNt$xwEEJg zfk^8lwOnujb9K20Njn-*^ei6T#;HZXj21&R(f8)4`WT!{HG|$9X_7yr8Jg{eR326b z%@D|Ln5oUIB$#BXUPwy`B*17`HW@hNzhoi`_oe0hNP>SzA|fUi&R7A@%qK%p!5HYT zC+r6828(;K+Ns7%Eh2KBP+4YR%R|UlZu&^%1pfk>AjlV(Fg)#Zt{K<7>7)zGEGN4Qwr*s zm!`~uY9Ke!CF@b+!+dY`L!la$&ZMdgiKIiLEvpPcM5ubEuTluZRTfQ1EqdHc8K%aj z88>OV)U@ZkqA=EpS}0klh!DxS_2wm4Ju<6}q0J4+QqW{&BBxIzOZhQ;5N~BB^FcBw z=)(Kd@28m|Og27F*G9GNdblqw(X~-+haMhCOLT2i+o^|#(-K`9)gEmw*`)`)QRg0Sei--} zC3>ThCsmR?tWk;HsAN_1!#X|ajY`%wKdje--l*h)=7$Y>&>NLp()_SV4|=1Ljm;06 z^`JK@*`kN5(+6D}C4+jnJ}uF;QEjUpZcIybZB)Bk54WWyx;CoauZL}EiLQ-m+x2i? zTB2*C+73NDke2A$sJ2rN52q!%HmdE?!=q`5u8nE~lHbSE5?veB)~Mvkv_#iNwRI|4 z#UGJM*G9GVdRUv5=-Q~ZK@S(CCAv1MZPLRfX^F0lY8#tNM$VTre-ocax+k-yUWKEs z># zwIWjw*UANzRpx^AtkjCao2iUUGl7Z(m-@Jw^@>YQXbUw}iI_ZyV2%Zzs29liKQG0% zf@a&+Fr(#-dm4j=5s|@_;Rz25cyhsELD0@_0e4Dot(swnkI?E)(f6$`u>)M?Y?s}P zUb)?|swI^5)>~+p5QM>a5w{&ZPlvCQy?2RGTVIYgCFF zmEw^tO>b0+8&grQBr zpPs!}uc}W?I(w>IJBA&nJ+=8bZQ2<+O`Cd+)3iH!veUFX($ln&0E7#omkjsPNT%gn zixszwEH=iqy;(bRJd$O0@3@e^7ve=WoO>?JqLif4^1_GB-){j7Mu zk;R_uCAYs7??1BGlfC3-Tk-6X#h&aX$UgPYNOtV^@?HYCW!KqTD<=iEfmM%dlOZct z3T1bS3*4^D27~H{6)ly0!ED)!OZ37U8Mo$gYUqWcP)vNxN}1iMxRRoG1xyy}T`ecH z1)0^dpaZdBML`OvIqW1+1GKI?<2bYc#?{Q1h4Omqtz()lS-f#g7h%37wSq8e9p7{n zT-2yEuIVBi-KaFK=_WYf-5vCjcvLx%n<6&D>W;;)L>=ts5mtq z-YTq!2)QEY%cvG)PQ7XMM%A5Ztaj@qFD6mqJQ8IuyhIu2-KItjF9`mH)hN_TBf^eD zA~pOD*^UT9CgoaQ9!ws8i#=q-As5$*I86h*)ImG!;hL z-)RX;Ig5zCWWG~V4X={${akc>TZtOpacZjJwNeW;;^W&Cc=DpxRKqK#GBxmdiB%Ex z(Fka&;dN6RHS))|O`(Ptx~3XlJ+)J#aC}>p8eZa>YIqIRL5FY_>yfl!W*j*RtWz;UBQ_!u+&sog;TW`c7VuX%w$NhV@RpMGNj~E zVbi7Jfi`K6-Ek|Qa$R|imqr};Ev4<3aU+4B`xamerQcdFR7(0s6-`6 zzF*m_bGYXyCcf@cjmAz{ZabGQ(TjAuEYED0CAgPc=Fb1fjk|(A3jK}CEmc9Z%w6~6 zZ5Oe%HcD?;?NmXy%zg99Z(f;Kfxlr1h=Tdip}=GYo9@o#Rxo*y!sJ;n^Gz`G%`j0g z4Vd|6n5?-pVCI`)LY{@{C%vDKpUHCB5cGmU=S2#gXF+GMHF{iVhK})SKrb{y$MiLz z7n-3Ha6SZ>#JIB_=M1KntNo^Ao&^)}Y{1Ml!^}0o%r(QzHNng^!$bt;38qQN5!QF4 z0NN8&^in{LW9@won!J*`FlpiLP24fMxjThBg7mLfk>A!qEL__<*kgO5UJmsqwEplS z?GMk-zSJn!qGbqXi^1eY3X|t&pN0cOY!Mrg$kI5_dKnK(2A>xxe4d|uxzTQz;tlV`zvc1Maf;t-#w%d6D;5MfYwNqcv&y#3lkeww{6Qss2+Ou6d(P{IX|jvrQXTebUt29AI|l2*WtcB6;3S`fK4@4fuCGYR$;-#p;Fw>!Dz9bT)>bNOL=R9X?e11~CcGl?=d+I9a+0dS^n$IYy@ zXf_Cnc}2oQL&2-~@C9Ri81o^~;4zwH#?{tSAFG6E3XL~@ zr?Ju$Sq9(qcN*yzztdQ+TtQ58?`5MgF7{%Ts$&%7l`PS=s%O?%pfQY0FYVVQ$0c0L zo)E6}nG%k0$T);UF2^BUn+LYLq#K6@Qy|cIRBa!Rs#OuLGBM#gJbJ@_uX8+!BZw&R z+;fSWCi09aann^Yro`?2Y)ITb`fqZ+z+p<_fb6W~6LYVLbLXla!ClGQZBBKt|6T&?f76j55J%TPns6mF{fUXL);fdDp z-TZ!8YrG51b^L*9s;PSUUbkT#d3gTkZwZ_6VtwyU7&0`PHaHP6=IWle-R`@C%ZR+deleQC?HYRl>Ww!WkZpU#Wa6s*UISd7hbcq|Y2Zp4~y9`Ycoe zm;D;=@I?3L9hcdScj(51cU)@eJDAMxP1|pQ*)2cgw^Mnjygg+>Od|Gl{P4Z1m)X0y z!6w$dwu?@ZQD(N)f23|jl33B)g{81GV6#N`>D+}P;wJN>4G6t`=*gPX$0>toE+aE^ zhXf3C*Tv5iIS07KBmVME#(dMpU`Qb)@#^}NOASuI*D zee3jt!*#9$ScvCkRCj~Cox|H*D6j(>Z}oe@2l7^jUL2IY6)$!~_H%BXO5-QgfCLS( ze@JA8gk?xfW+j4u`QO z6NKW%)b{D`&O`dvUbh9+js8!Sm?=pbGu7>Owf6P|IvErR*II_O_)h69KFl=cRna&g zhimc%%<2e`9#s$Tev;bTnjn(J4Mcuu(M*cw!$I{bQ?ShB^m0i1LK=_P(5p4p84BD6 zuY1wAEz;5NQxlu$d#a<&UQyd^ikfsZJCjWbZ$=4E;e+icU#$^B9aGBpvzS)pc|FQn zD}w++Ih#DA(@jb54JV|e2k}4nlc{N`dNIs;kdnTQfm)j>VLP!li#STV=ZUHPK?Z%N zeq|j-xOJGBeYs*f>N6Undlo;~s8}!GZwME}YjSK7xpPK6#l$)7Z#r8899nN87yT5g~kfm zal+isE=LIGX9}_0tPlsvQiwZ?A@Qy5s2dB4cfP!@9x4Bp`POlA|BqWjVhz7iimr6F zMUHHOzFJ9YOP?gs(UjxJ_tj#a`IPOGz0S`}*bik15zG59!#QLP)aqVhDdYT%sYS2u%y%_%3bUg^+3V=?D`rufkSLDE^ zfLUf>ab9S%+MJI^z>+DsfKI{>lpP=c#zY%4uLg>Ve1*DQlDKe+NqZk1M+AaGbBr?@n<6heQpx3(2Lgu*ckng z1crpfEi`ePm}=yLcxvfJauSJtqLfHtvVnM-j*XL`F*vwO2M2fQ;NUJD9Msg{5E=}Q z6b+AkQzI}e^@@=N27qB3m)~0q^DA%$Zj+bow=!wN5q#1=-dtxp0g*l%(?Ruv5$u-X z(>NnCUcH4cv+YqzAXi=oU32jPw7-pRYhsOTdF*~ik+E&sG>M+_Xna8?ih&OhymTem$a24+Xu-_v`LU6iu zF580`6Qc9m9no5g8O%Dx0BOQ0Zk%EuTH}?W$TA}9cHAZ-7ew6|_2{-X7{hONH`YmD z!;6Lq61kxbqcQw6RJEvL8$Jo{DfM&shRogHbzm0MQ z(kY=t4i_Q5qU4c&FW7w^3k?@hj^hnzYesw7YB%;hkKEnZwC`EjOh})xOj4iiZj9Ak z?QVpM5^G55FKn|bmA_C7rziro6U!U?i$vYMLQ`X-_;t|t<_7o4MegPDM?{AhVb$!JxF!Iz z!X&i2d1*@!%@c&KF0G4{^Ll0VxKjq8M+Rp@8*f7B9V$B(%dY4sS4*#Kge1VimKVWT zenL>o3+)!|kfA+hD(ayyrCmZ(Y3GG1FPJVhUKo9t+@&w3$a`TT=Iysw&gf`CB%Z>i z27QLPygU}yg?j`O8+fPf0iN^ckO-X(|^GzsC zX5qi+-DSNL-$bHBzrf+CE>0rAH7?Ec$ZDP_UMu3;#M^inZwI2Rza~<|&cszQP7Tv% zZb$J>5}0y|Y&|ii!3SxNoq-(akCmd_idYea?d-#f8NC6MC!s12*>+a6@T;UV1bC-5=U)hv=cB;v2Jqk?M`?ew$H*lo{=1xn31IKDrpR2{d2 zaj(yI_}pLf>jsp4uS|iS`D^M$7PFI}LjVm{4roE`BW*j# zrSeQ5x#3YLiO7)>B$67w^P?U99zh^SN)_k@e`y$jGt8^kNf7nIchLg;5CKR#3X+g6 zT4wJB#gCIXEDb3s)=*dD8bAna^|!Pl3!c*yh`S2P%JQ?o21B}3@>efOq)H=2sWfci zxil=*f!agsom6aWd!v<9lxWn8(*4ZU7%6ZJPLmIu#VUY9aXv%Jm3mpxN!q1 z-8E*jQa5m+H6U(Eb!k}c^xPNuw-==*zLuJJ4wcH!(Ux}AWFXsg31vP*FYvZVoQU7b z*hW{o__W1jl=s%j!#Yl9K9#eH6ApoW&3b6tVoaX4X<{a0?ShGG1k!EmK~WTL<-{pG z0j3^`Kd=j@NW&-q1<_gZiO@0bW}{$+DCokGyIa7 zi(Pf8>-eHmUQ(^c z+|OjX%y`r-nsB$UbcxkJT?qqHEEQg; z$=E99+*AdyF=a_SH;5bGDA9Ug;I=>~pu~*uGHQuj+{Mx%zJy5@GhLLNFsB2;I|tr? zIAsy(!tV4%I`@hjIx!jw(1f8d=T_YkW=oc&Q2iu6L5*nEnyRZe3_T2_P_U>bcWK~;yEJgaT^hKNDODs9t~a8Zn$Qeygoa}Y zigt}49GW?(SGnWBmMuBM{$CyHT#NS50k z85jH}vBgyC1*S`+@z-NriF zh*$TGex=p6VYL{DjW+`Wu{8!b6gsi8Db;^XA~z5lEeLUx&6k}*D z%bgTWafTW#Gt$KY<6Yvi@utL=@2l-DW`FE%Bd(o@*tFh&LVq zgOUCW{(MkFOlmHdgW-@K0pLl!J2Z*2ww ziYGY6IMrEaDJ_~wqM}Azol=nZc6N4lY@tSd9b=&l6u<(pq`--6?sP<5J_=GnZ|nd! z?gO{9SC35dU`58<9yM#cL(X>jML9P)c3( z5JrfQFo2s!#ED#X*^ea9ED$TS`S$psFZ`!`L0{A;8J0v$bM}Ehu1>VKQ$n+lHVCEd zF#O=waAdHMfCo5IBgrYqV0-jLRbi>z#s-#b%wa8D>lg3ftX5F9W6kWyJjKsFWK+jJ zUf+TQ_(h7GCjCsc6vpN4*ztN+OS9wM3Msw3+hO!-?!D**5vu!ngiAbwc$=(WmE#~~ zgb`hnT6d;fZbCX!Qh*8~6LcoY7f7}Rs#UG&^$fkor$PHlvS;x3)~I}Tk+78|!fe2y z@L{X>N6<53n&HD%#GD9QS;$BU6U zVAd&A-^Fy(nO9M*_ z``khwRvL&2Qh+s?m1xPKu#!T@KImh8=LdP^$jpM25+Mr=4KGiFLXeuE1hmP5flaLs zg_W3y`dSuNN+HGE$&?WHW@8JYdrSz?D7hyN1 zRX;86fsX&gCy6443dY3Z7EvWlo+*A@o)1R55mi#-*qJn>WXh4ov|^$uHaDPRR#{4H zH`Xd1u#KQVdH_=6a`~{%a8$CmP}1uXYSJ^8G)0ByXel20frWfYj?izZ*Dh9OGPf;tYrv#->LP^>5Mve!5hJ@eT zouUHR(gGnsurDE#L(E6XCK#NV#A(zhHLImXgS5J+>`rGuhVFrZL7&l?Lh9n$rN9#H z(yC@D84Bn-sS(>(ZYbm@N1-q-3d#iAT@)1P7WPT*ntDr5UXj3og$g}CKOMaTgHH!~ zK792m{?2R~(=9#arZG*_`6TNgBgDGM_4aA@q%s%qxG4|HzanNxby)+9BgO z(A<@__+v{ifu9=;YuhA~)Lr{+^~dvJaRu=agw#FXf&`lSbk^C!faK0pm=ljs+E1@* zEp9Hav}vPUD@Cl5aZH0v#z4Mad8ZFi#<-+-pw)0KQ5Z07#-*=u4RV(?{z8 z4o_p^&s{q4=PsT2bC*v1si}#-@_?B57p+@mKk;vBL>`oN91Tp8#S~wDbj)HY{Tcj5mO_uVE@232L0)g@93Qp z2Vpct&Vc;GMDrTBkgK#wE@a^S>k~L!NF$T6LV&60Ht=K6w*12(e@v=$TBTLRmNOo$ zg^IlPod3f2f$!DSXGwn)ZP~EK=30Dr;3n zZ6wA{Noap2t7dJ^e)a&>pbE@IDxB?wy+8ut>mOL%(O~Bc2rQ%Y=2&ly&ZwM_LNm7d zfW5s+4i@)BJGX8V{)ZhURY_WjuOuDBw>^y_Nv=kO4*>H42ecBt?(-2vI}jEo!#*)0 zy$cDwc)Dec3|*7*l$K&p2%{j*UQTVdxf`WGF1H_zi&sHdm2; z)OXFABLeAG&iYcyF_IuttngvdDtQ(8i^Ho33i?^Wuly;%resbQ34p`a+3V~c@ggl3m&$2F*f#LHaw(IKFp6}gr%c=-9 zJ%&Avsd1KZp-EPwtdT2$rdr3wOUu(4AUj4uMBnO@=Vh(hv{<#WmeVQmRWKe8-AmfnsT>gerA^sMFilqgy)R3&=!F&iIB}|g^V;86pUw#^3DX*OMbT`+!p#i63KMY7_w`e( zI9Z3fnoFk9D(_HLhWlRnP;i;O^@C5zP@YDw+9{VA!3h&0b@NO>pZ*I8`bJ;(>SvFu z`M9*nH%TW~EQxy6l5}t?6-a=mPSb-0yqjrDBtgRV+63}ZfuB*~N^Yv?qtB_K-(+h8 z>3w-F&|IaDQ*IytMbjzDC42WJGoXDxhv6s(n{CdtolmBMv`*(@+F%6pKkF9KYeKK( zCvst{QfKG+AZ9H4)|Cyx+=4hkxEO%M#&lsw`@~zMffggb^Tn2(TA5d20Q~Pe`TZFP zSuM07!w6y>%4gs&Db0JE7vjt0;*etJWHxUZz1KAK;FeULzbXeP)5YpLdl zFuM~F+kuhS0k(PB8z!-!#U?(yeoFI8YqBLC1=h*O#vmMq0m@|v19Knpa)ol(ZoDBQ z6S{P|4>cv++K*Ua_yAn3X^#))n>kgt{!wfZaPM41pvQp;WE^PUv3197Blm}_Q+me# zSz*TKq!y;upVep_O@rr6jrwoRV^O0_XRJ}}{HK|;QRaBoD3(5~u>fIAoW+IcdnbZx z-ZP4=-b7@GCko4=8foRlClFV$3fc0=_eUua#xy#uD34kZEa`_~EChBSS3`XwWfUX6 zGuB3m$l*5#?`#^zVvaV@uoN`29n9Z= zaINH1%%Fps$3psdOi=cJCMa7zL77&?#)J32Cn$US1Z8LTA{Uz3AZ|=CL(q*9K|na19CU9jnHnR>Jz^7Qnd{&+@jUpDK}%mH@{?~Ra&WFf6gPDDTNhE;~7 zHe`@pH+UCD#5`pDWu?X_#2~j1MnU^{23WeC~iG&86{Oh~*wH8ugw8mvR@v;1$ z^6GpP`dr_^I1Sip<%eSiR81RN$?vV+oemjzU4+raxxtpmwKHwe?dL5zPb_asM3&>z z;mV$N{YZea7q2KE7R;t82Fx5T?0ZRQajq<*JRsjXe-j1y=JrxHY{OJ(S4i=4CQl!8_d0>=@)|a z;o(MJ3J5M~(GB9E6y0rzaO4)G`=5Vd(ML)eZ!~Fj9KWhiz|N}%1NSHX@SPn~u60Ir zphA?93po*#*fvSjkKRnL{-;MvnwhGGM!?9_Su8{N8BW?VDV!qrTfZ_C@Vf*4UE>Wc z(22tX9Hqd0h{}pQYBCEqm`?EWqt+|>k@kSV(SqP&RmWQ3teG^d)fKMe;}COO5DnOdj^f1bd(+C+DEHk~wU`Su z)+-&JIWq0zen1b8NI}${ zrEw(otF2L%$T7iQKq*2gDbZu1y?{;iM(zbv>tk%hBU~0KYXm`ngbaw-4cMNp$Zro! zofD=w5#Ec~?~#_?(Vj?@OJkeac-sU0FHJUg(=r;=Bc%z1DMWSdNPBMVZ`1D%U;Xnz zxk2dfkgEE{wNSsfCND`|kcwfZ-`^I=QsQXIM@Js~#x_u_h;qCvzbhBE%Slg@-@7GP z^vsf)V@{M+-Xb~Dr}2iEANJ17Ht*Svjyuu8Fi}{DboUBoSI7Pod`!y}GYnFsKBssP znwbH!n6Mwg%!H^Xqs@-U$BRPSSKm&oPC01)Ub(Qg3yAm1O|t+w_hbW8hiDqdU+JvV zC9)C)a>LOEyiA{EhT177y;kMi;)+gD{wf2L8CZ2mttu~BK8zx|X5h%iFA^;znA2vC zXx`m?_Brn|wp#viD~C_Cx^36`u}gM$1$=M@<_2xtFsYs!OyLHvXl~HX4HH;3z7#-) zr{qM z>N&Un8E){bm8+Ncaca8CJApM+&Uv3h)|Ka1crFlpvs zM&3OTo3#=fEK!oV&7HmJhD)50?5>pEHTGg+imJWjlCuniLDBpX)2yh2bffIEWRgm# z^~m`oVeG5CJw$ms>9AT&Abic749APuN~zW&KTsLC)!EI>l7p#cIlX=CW?RSvNBuT` zGBV59=P#qw)5Pg9P-7ZV!+KXcN4GP6kL2hbWS--kfY`3#en@iz4cv=k{KO(eqY2MX${OLx66RXK8tTeQypZtG75m6bw3 z!L8ZhZ=tqs?H8xqTHVRe$qUj0+^R)GuWgw=iP*JOi=CEfBYh{oh(~h2bsystuJWJS z2EZboRdtX=WO-#Q7pb;9K&knV#W4IEvUYqznB<3NHae%zO!;)zNlli^)(#Fzdv^9# z_hnkm1eXwHgrLb_yPSeadMCe}JLcX)u`c@2ku#$rqhYk=BLyreA%~Q|N+acuLM5yt z5H)#bO$aVcffg$i{K)7qO7I3!(6rHwSYM=76=sGhsrl(q?$Q-wp9Whsy)E>P5mG7f(^{A?5~_^3G0$1t+%9 zSe916QBg-_VE9baX_n7jXPW;?)9?t6oH;644=am*ZEkv!-i+RqJEp%6E1af#5RJ zkxA5XdTbhMnLCgFAG9vGKsLT(Lmn82AEqdv&1>-3!BJlE^I1HsQ z2FRXb8!z4PzA?$7#O115u+-Y3u*4HtbC9d(okDpVls3Z9CGCWx4hg{Vmb&w!Be>4f z`y>2oLf*ZuRZP#jtJwx1_nO=5xfMCMrSM3-StDWHlrvEwIordaU>>a=YwasmVel<} zu~{ex`e9b7x(g&g$*%apEPP{nI(=*&VxjZ2tYLjr$@Iv<+OiSJp(qa z(avQCklF>94ho3|)Ge&Dy5svaP=Z(NDcN-n9RDjf@J={>DLy0hjAC4Ae%HD|;VXh% zia1;p`!iS=-elr}8H(}%2{x$KN_tMmu5zwyyw+{MwI2Q6fVSgo9CX?Um%*RaTc4sq zZS%?eqFP=iAH&XEvmHv-Dp5E8Rxr0En#|IO5?t)8-3n95T=iu>I!a6y6l88>Xdphr zjUSY8thK-s`*l_LG=qVQ%EbRbd+@cY62sO^&BT!;4T7+$_X<{a&2tzY7$sgc#{PiciX2Sv|{J#5fLxU z1*-M1-~iEb4%r#j980vcnycrIo(a8%HHlr3X4eeBMu4(a8FlZE!*Xh-E!Np{78$)d zsxbj)>a!B$6}CN1=Oy}FOMR4~cvQwLMzUuY9dE&t(q2dxjL^?sIYU4=7>i)2E!J%$ z^4gPPjEP+tY%?$AD*tFP{>12-WZ2% z3cEyDIioC>VZ37ei?Wns;QORD9|JEfT&|`6ynA z*Uc@mt)FCv=8ml{lzVti%`#sMx{!u*_6l#w8TzeUbMsbP!kp z{{pcQ7$)T2u|x|`=Lx+gALqk&$OGu)PD(K9){N8~QGQz!C*tcA(u89>X5*d>o{nIn zUTc-Wv^om_tI^5o!l*?Oj}*@mO6Mz9LA~r z(CKQL%WDV>;f{0~?)t4xJCTdyaK9VcjKVBMJ;XUN?WS}Av@_Ou5Sm2t=r}3Kw(8iK zRqUtev_`7eh73_E2m>K;+qw2sSmp-x zbcl=0SV$jQoRAoRMsr-f$YYX@W>G^VG2bAvDOve^rBRqC)NQ2E z=HPU%B!|$ZasDbl4oW}L_av6EuBW}dwKzMx5U%<+IC<$U!d|cJrqFP}1lCpvNwLp2`cNEA| z#w7?B_o-F`F*Gu3K1cyYpc7~!W?MLQ>fd7Kd)ZN&60b+#D^Svl?FxmXK~3K>o|qh} z9QNW?Xj>=ZV>_BNX!Mqi(A0{xl&7!av@toG>3!_Co7$w9O`+D%jPl1~js&K4;LXBv z5)Dp&2{m@J8j)c%I^~Mc50&Hb4wZu;s#fu3v%l3d&~{1zqnkeHKF)Vwft^w4%07{1 zHc$$%kMl_eifMqLNJj&sH2w!TWpW@a(%tx#*CyP(@g|MV+dOMk#?^3Fh<+SI2;5kR z-f%a2AAQp3Y720;lvCtB8kT zK`gl%$e*%gu>@e6S%O$vF&fgYI2EQ{JELyuGBJOgIXQOV*o5;{*z| zAgi*53@@MVMc^pbi$l0%fuq<5j@U4ItME>I4dogVHn9$FU}ovI6O?lYx=lATJ<>e% z#x#n+1_$g>ruuZ%W~@LdcJMX^v`M~(J6bNRo{PJG6q~mc4s=@piA&|3Qzgptd@!e8 z2K2~n8hSv;#UxsiCt*)Ab7bwC@z?30KClqEIlb;cdQ9nkUs8W``XZMuE(dAwN_Tnd zsyi`1v3Ih)M9X-`&dV!9w2hXDNwv}yQd|1V+`tM17)lEWfYZBmTSkEEGF<6M(l3k1 zv1oyN^tQo3jP#iB{FUq(i%WCDVqpVEA45&?No^vXkiNy zkUD~(CI%2Gz-3T0K^*uT+`A*@C<_-VJ;H_?7Um!uFnm=n@*UB;v$6X%?3)Es8pbUh zqe7w{OzA3x>Hnf!7$Oqm{7*YC0`~yNPcZunsxS5u5H5MM<7c6Y{Y=9-qXWak_1C(x zN8*>d$|2x_(LBqr;oH|rxa}P+Hd-jx=y;8Kd4FaPb4_B==~n=*1;a#?`1-(&+7*c& zdHU&FWhYHw^8rl+wb=cMFPN=DAN?c5lI}^#7xO`=GZrE${gFySIp+Siy5~J-6&W}M z^5i)YY6xMxIZ-+ZL4+28+4ut~*c*_r>66jvzAe?v#A@j>Z6GFsifL!j1&%VX9Tyz} zC65u!%l8;?CY226sQjBK%cPRaU4+%Pp_xyrf6Xn9qO#qHX|-g#W|1>yp7yIsBlqU& zV^kl%IAdEl>Sf!(t@N_)dc{KYN&1SXF6!Nli<&~Th#~&!G-lnU6jqqDW>D1#UM3$Y znD`bhXoWZV%PF*k1}Ow4gRs`pQ3#?1*<)UdlMGF+^$_jY22tcAPKAEFC`AV@8bIy4|Z$Dl_lT z9YZH_WG;iYOmfUv!|?hs{4@vJK|&*xY!fm94KpWQ_E97(Tf(!S<|ID<7X2L$0orSs zXl`*|_ic2Jn=Snux|-VrE`Hlo?6zfiQ60}iJA@^HnxzjlWoI6PAW^ki2(gW&{k`G^ z9Mw-7M1ZhA%0mW27#_GF|aKHc8N8|4+8|T=mCbajI<1H#XY%-ZYdK_!O+=UQtu!g+~gM?99R8OD)V~NHD_IH zy;*#-#_G&6@aoOxOH?cpk1JF)p2!8m=jaML2hjPFeHj3PD~lpCn`aW_$%OkB_{80F?fGJZ_B#(>FyhlW=DvJH-B`lfvxC(vAq3N>l zhQ&2dB4Y+0w5k|#EKnojp=DE+QifhIZh1j?qR4JPg%=2vX;|uQ7Ncls8M0v>yr&OP3%lT+9Qx=GF3B7`x!Ct9I%^&>6 z6U~}ny_V5+%RtPQ(G@47gf(IQYVHx*T6&Uagh-8OnFCqR9aDa&K~+CHpb$1`L?Iyc zytIU>^QIAb!?jO^N3Ba)0$lSw`k?+K`75y6n%zV;j3cuH^38Eop_g1HuQ7R*BM>EJ zTs6&&UncS)4o1TqKV|P-Y)RM=ykic*EJ4o5p|s8s?G< zv}|GO?xH`po~q28M4^NVJZYK&`iJ<-9DjkZk~eYLgCf8uTL%CI=y6|Xi$y&gS){~Ax0o!|8Jr>}#V`?k}ovR+&`u0kS`N!K{R&=oy1n_0NmJoKnq&<(`s>)82$`F90xm0*&l$b6xSrb z1o2l+uLgB)ZeR!C=9Tr+``b&w0vU+;MzA!z(JD_fAAj%;FxujELRykC};k$r6!w4jE-eHRCjdbLox{Q>Yrr zHepOfi|?=LNmln1S1r(FDdcF7pnPxSgFUIL)fBQOx%F;VQ#4uQZ*(+Sry!VOv}ke# zrN^+Aj%!4d(PKmi-(W9w=Bj$(Qy-+?kUNVjdq-`IP%>+39zdp;wfA$FHuoqrX>;}s zrmDB0%^M$?Hph7y-ew!oWCoo&pCXU^`iMMAuX@T5@+rznqN2d^MUZrzLdH2D(x12u zLHVA@ueL}HgeYJrBvB9D-4=!-grEVysBeU}l*Gq#L-m4;sosSMvUajm>%@=Tqdyt+ z=E0mxw~a@shma~=rZ=tXsZz7PY?hVHqD;w1y9<-5(omZFS%Y&mk*O*@$22&TON~r5 zZSG}4EiDDHR-~?K2`0TIBy+HTx!F3dzIcosftW)Yh#zZrQn&N$PDVfo#jW<96b(h}>iI%$L>mHW_)EZu`W@Z+WRmc_1Xi3D)cq36uGd&5QVy zZl>K!rz(0gg)P3cx)S=;)}JHeideU~dHPX|(|{H*U*i7?yLbnuHH)0e5Q{j#37i zpEW?_@F$Q8*)DXNCc3h5>|J0*hCI~8)#*IWL&(ctPN!f4?1sw?x96R8hPy4!L@wbZ zlq7gj5IDv}^#}_(nIl(=Ft7m>hESMLMux?dkYSytt!Pp#Ob}yDAIZAFRnsIkcJ0T> zxCU-O6GO)jz5oF>yA9Bu$%3^<8JGv)3q+|_#9=Im3lQ38AD~TB2Z-*2k>3^@fo;>H z<;$Y|g7Q~nqBCbmSB;`x4J*dr(fEp7ATScGVM2@muMxt@&rqBOp|hBgmcQtt_AZk! z1Mi^?N(nk?XtTkcNQ`;iw!b$=3zh~bcSgT{c$yj!Y$YNRoMH%aM1eN_a=Oj19m0U{ z9=2)}RiiknhUPtrYcQopnn3_>O7R_?5Sdrt#p52rd|V+wg1NlD3q z0Fe#^gKgK$S*xd;q;fl8zfk0+cVdEVMCA05!O^cbxtr79c$O1%vx;!pt-SgZ3#FD&25gj;&yMk_UT-gD;Sf=L zk$Nf}w&KDd`v1B+6F57nI^S2-y|?e}zMbR-0(3|q)lGm7S$avQm$0RR2}BSAVHZtq zx^I$BdI`N`K{Oo_6$CUm%*d!9%rI_<;5-*p)YehQ1qEju_mRLulK>RYGktpD@h&;Oiax?`P0IVupY=m80+CcPErOs^Q1P_nkCB`M}aAt zhS308G^A%p`*%ll$EyZX8DVQSP}7wZiz4v^^_bfy?Y8?XU@ZU^R3+}sY;3MMtFv3mTb>WjOlHuC^9;1;MuKD+ZLQ6AJS^F=-W$T&hxz9`n3XXpk0yLdgE z1!A~La$=YG4!Wf)`mdBix=iG@XmT|TiSe{9{=kErOsTSy3G#{k1lrN;oJ>5CBby&i z{1Ac7cTOhmfSK%STv*cgaFxl)g#TPlCRJcgCb})(pMqvwNA1`OdXTu1n5)>8L|CEh zM|z3c=us&4BYnwvJb+cBobk4UJq;@0@-yobEQ*)0`PKItm5%nk6MS|jY{BvTLJF2C z6lAtG2pKuQX$)fZXpy*brlE7rVso`6-hge_Sq@Blu`H3Wp}Ai@{uYkaUZEt z&*MJWprpxb;A8JfB#$NcD5(XB_c{9-zp;qJJ}`yWBqj6sXck3rm1j{_v@Jog$+<1- z2|BTTs zKPF7sq1ugN~ znGd>3=PfAY-vpwLwIj}#bZtZS{_7j?S}&nStloy4EqxN1Tz%>N*UQz(Xu_;MgvWd< z@|>!jfu)5qY6Fq%3y4S+eH2jz^M-9l9XE&tP4VQ~%`=j9T(p8r4RK4#f`GI1X1OjR~lU6Ze! zG?^LlqdPt_&Ow`nHwLvFtqMalh6z}s-#*9-9wAhs8_87I$Vsa_|Baqkseq16jee%& zWF_Tjj((uzBqcc`(U1O7$)lBIA;OP7p=3cxc}7ABETMuQy;m=$(iq;UM;@@obdqWG zm-bu*_=u1n-Ku0xNmJr~M^0BATCpEJqvTY*71c*SD!(=Kc8$s5@_D_`$cRLb8)HKJ zNMDqTvXs|5gYz1{c1QH?|1}Q2!z++`w{!2z4k;~C#P}hSzzlZNHf(nWd=H&p!bf~X zrmcPPmnZvY*&qA5MVQOKF{p@sW4&xEEQiKqM{-YDVwM#dX8X=9GE7lzEC{%$HuBK4 zMS;;{g`=TsfIGZK-#DO(L!#BJEE&_N3fyxNkVViTV72;QjzY7x2;x>A7xtV4ifL;! z%%9#7afYr$#96X8JF6bx_*Ef5WF2&2YRVyxu3BJpR>I3aTB+#ZBaiUS2GD86DjRA2 z=PHJ=)XDo|rgl1BKgDrPF%uHe6Vh22%7li45+PM31MN*@Gpz`+D`&$QE=i(#0xd)x z$4|0sM!s01kvH3EG(5@4x|2quH6~U_ZQ0D~G>rzDb*mPrGV;(@NuyB(rqKXAdCH?_ zzcMa}p?kqp5Itp$hWli3%p91~eVfFj*=U_ z-|jkH4yn5-3@_2J?&Q1;`Gq4sb{`zkj-^e>;YV+V^y|frejv%V3+s_h{sRFAhb1mP zJX8!wCJ_qL)kZ=Aox=gy ztENCTG0}cl&H0`+6jLTIe{*@67oV>d&(&RFfCemu8vJ0ihPQ#u76Bq?XuJ@Yn#M0~ ze+69_1C*(NGefAEq+`~|qNB)T@5jfLMkgs#Wrg z0_GC10&%Z2g>^)?q!pK{xLLdZ1cP&R)^(_MHO2f{Qv^kPK?G>hMpXsQeftyNIeVP$ zWb%YpWMtOS2>##J%?qb62vc2d4V>W-xCJs#V-$YHC}eq9EFYgU zmL?ZUWB#Q-u?U}n0#aJ|r?y&T13!h>K!gvuHE8)DP<*9?H|u<}r*C02&xlQF-6r0Q zj8j)@LoXNtWiAcDsXnSvnmYk=R@?Et06d! z3k#LT{fD=PC$ST2!cqnuptqFylvqm80Sq8` z6KRTXXs~k=M7$szsIL`FQN_4@@iMMIK3f477oIVNE`oG4g|7snitd&Z$zohp%q1a-J=JotN@8|YCb@24*G&AysH0a8NM+@@=CVBX+sR{Zh30zFi|LbnKlM-9fBu)EGUj zpZ5gOo4?EN1A!}>|4*MKuu8uNgd6)k##Qqk+nD{Q`aQz3*Tb*{*k|?SyMyLglN$Rr zm}9eh&Mtd4KsS9$55I=10!&E0_$Kd*G>sSUzTimH*nRQ4c-+V)crlY}{~4oaOS<7J z%*`+CH4Bcj*A+f3y$pRp7L8yOnz}ASEo1W(5ts>b`C3c~Kxotiim?`l66Nrb1w=H^ zDt3r~kq4>qrZJ?Vz3)y`_J{3Gn`?L_32B#lk zG&@mzw^=k}n5|HBQ%)A`k9n3NLFDgZP!gnA`w|3n*@861-)RF$bu)O*faTLN;W^*i zA;&}EDL%dgNawk54;_ZVxIbmV%idDk#U#>;bijxRmYhTnshgRIXx!PfltKAdRE6%e zyA_m?P$$?^(Ha-HpI7j+=R*PveGoVl6`ElvcJPNxpSyX>6CzTktAniN#ZBU@;ZDO? z?Cnqw63LVVbE1!$G}d5)!uObOi7}l2h&zC;lP?t6=xdP+-eDGzP=JqA3P^?(^hop6 z=)k2JCj;eCEDFXRd08jD4&|rO?fv52Jb^?3#tl_=FmhT4BUddAIT*R{JD)L7t!NO> zrKSx{!_q2(uIWwza&{m~#yqe_Fl2;UvSjj)#2N>BCX;M4$QXu9w?U2lzK_dAJ5I@; zQVK7EQU+lT&SB=EFua7DcvQQwFj@1D0tbA#VLM?6T%`|WQI`2KEBQTE$w5ay^6I_R z5(@;;nE3*;H&idZ0C$wsYktJCwUrdt6uD86wWdDd`~Wss!nW;2%4C?%{|MKiJB)fS zSexaUZ3AIu6t29l|<=!JN>qg!ttR}e+vf&ixD>|0a5q`4-z$-UxqrJgcpq;%{- zy70HaN6S^ZKSq!1m_GI=&IIXT5tYWVNl6gg94C%vFiz%J#&KXMg}nLpJI1|eezf}z zIFTJP12->6e$EB}lnMF4%TyQLPOeB+4GyNK|BCy&RZJ%I=Y*TlW=%Z!5U>?Szt6fq zD`s$~qvV$aON2(~!9d|zTa*HNTFe$KJ9-@xEU^&MQM8u5<#-&H%<1v+} z8i`NbG-rh(H4O7`vWt6wjvUmk$PFz%arTQoCjGtl&|_P8QxBrAB+0#grFFxAq0X|k z@`$XJsWutTu6YsG%EOewTG0Zl>=oyRh1?)lnXrmD0h2;v9yrlmCW{9)Kv9(@3s~Yz z7G;!a5*sbt#|9QB=fIKmDz;iCUL}@_nv&u2^@$P2G7_NZ1ay{KFp5+OPBni76$~5* z`92#CU#+U->~6n-0jJFhs63CR-14 zBAfAa=c)liz)dGWEy%oq&<_xKn_-3Vw$QVUi-m7WP^O@A=Ma@y11`HoXIOz~lYU6rS_;>c z_+py63Xh8kmWSAk#-k=*H|rh-)e_E)akU?9rk7-kK`=VZXUV3l1j-H zbHT)?R(LOdlEVA=^XWfF?_c#Yb^Zy-pv14~)CT!A4`9#|{f6*BSsFmXpt&~&EyNLw zfxWo11rzB07(EtuPQ4&^76pJk#+{w4e?G=++KPO-WS`BCF=oVh1!Gp@2Qg+$J=Gx>rjJNLc3RZOr~0#TTZ3onF))W6J5-FCCisH1%!0H{l024gHP#1f5& zXT>t8t_C6=h|%!nd*d5V;*1`UW#p1%WUMxpd}NZ$nR$sM`^*GR!!bh_En?mSc(R$z z#y;tS_Jqpekb>`HaWNW60DXc*5+J31LZK)Qk^sh5?aNdQB9>%n!2wAo$UuN%|AXSG zV*mVjR@#`oUH$2bPZhl<=K&- z5sfK^0`f7Xq>S-9_mW#S+0#-t+M-*O=F~50jPBsR(8g?-1|klHA~H#{1+_~5KtzTq zC2byKWy55%I`YFusHzf8G(zQ!exNrt`;soWc}1;K>yruk=8+;D{BvnNBrN>NH5jSH zh{jOWb%cEd!uf|HT-n>JjOz6B_E?A0L-Um2E_<9RH0FCe1DPW<|uxCf1p|A@{9kfHx`;*)zRJM>LQQtD$|OL!!QRoz3~R-<)r9Ya=6{pX8MI>ft5ae+c@Ni zfUx5t@09+;0N6g~_BmJxi5GdH+vk7^4T6K+UVVVd9yseJFm@&nodTMWL^@#5w+L&+ zzClNPl9%i$$34*2wfqwA6krXNc?0Cs%GfX&52;v+C>3)^#RQ2+EF0GC{VJoIr281_ z(Di9Zgh5wD7%AgW$bQI@N3# zoj9I63R;ME2jn7HF=~WZ;0dqMwOj-NzZf-}+&rNog=momVO})G_KE{CBT>WTmb)z8 zRRd&A^K~H{s^9J6QQWEa12H0bY!JT8oHIb-6raXRWrm&p*d4kh{nwvq4x9aGYQ(28 z(cb2J-i)a^)qzV$YY*`<$ma=Bj4Ovf>HQbjl%reM-}oKU9HdLR0GoHmTmnV?4=6Rp zWku(5Wz#*WI1&?FC^o@_z0NCBa*WFVu+Ad=6yP%phu2v?S|+bO_y(P+#3_UJu~|Nn z>Ui!$9~!5k!dLtlk72AgB48}?eNO5|T-)U@#%b8)a-USWfO2BRDj};Ex9CKPi?ukL zOjsy_jR6_AJQnXrFc_7h@E$-MYsqud#j{fb0Vqv(D^-lL9#<%*{O;*_K|iGBDLp_@ob9*|=ek3jan5I@#+eGjM)qXuk6N zNol5{Nq+y^LOl)_it#tO@;8j#Tled$N^&oEo)xBapGIq(}Z#p z`uTUne92klp;tHu*xMlqb>-+6u~pQ`i8RP6ravdJC47eF?jp}d#GJ`-Z<~Y~Z~}15 zZ^ij3+{fpq@Je4=11V&3GVsB=aR z=ox;jo5FZ)+l_5WGAT}XEN*vl;Q5O2nq<`p2?rnyQW zzTl=g6QqZmR;%1itG4Wwr}gF+(VLDxS7#C&Lr>6r8dMwk;SeDv%`9zF3R{Eea;Gp# zvT@iLl9!+6hLj{6V+|Rk+h9X18f%W8019RMa9eS1OG|k3u$+dnwZSY-Yll8~4=LK0 zU1A=g!>5HEoKX$u=SQAL@i&XK)Pq`9K;#*~jYH*3E@>1ORD^g(7-@AM6A5u<%Ox?> zIfSVcC_*Q-=#6|}t()G{rMLNfSI>N?Q4lcE$X0+}~Khv-g zwj=@~@>QH+NXhaSTjZ}GZ9KxfDb6Qhr_AUN+8xTD1iO@G0LD=>8;y@k%TFy45oqfx z%&+2TkyLWDNa}#2MaXM*ta+$VD=Ku|*v>FzqYCXTHZET82X9b5CP@w@ey+E$Ll)i6 zzKJ#7&YRxI-|UTVyxu(JJf5$|DSKSSW34?Ri2;aVMwn5#jFrn+IV(swwF+jeU}mW+ z$ma(S2YD)+06HLy6)gc>yxRdi;U07!@W(*cEspF2=zsWppBU(BAiv9Z?nE>yQv$_% zDtywULM92sNP#snV^vt}P3}T|Y6wQql0A>f2^2P~qTUbm<|P6Dxmh^iI8+c%gy~0j;!KUC z)3Q_((DTB2x5s3Up4)a8S;D+m0de;qe}ETLlLD;Yua_XB$5gXxoxh=_A{_prm%+I6&Q_q_+B$0d-T{IRzlZ zj+w$=$pz9VYtBA{%P$co_J~C>%_$_blA{BN7xpaHUk|R)et^c{%6c#x6o%(D2A39o zB}2TjJpB;6!4ifon@@w+do5nzhIr1PD>r8c`V9q8O1RpAnv9Zk;Tm&s;3BP)5^J`2 zCaT{!hhuc%H(FNkrYr2)8RhIfe)rCC!5oh*t2sC#=uiZlV}p6O`XDZ$_$bDA_qzeu z^k@%1&YkEvaR@!S5g5AAE$O1rl>3+lxNK>r7vy3Z>|--REF&nUc|aRkkuq*Po1$P# zgYF47VYc)CUA;&h(2MINT>OahP(VU%ETSO_>wVER%pr)w7eu;tOaDXGdbEWD#${i8 z*O+7d2_Gri1v~?POI##+_C3JWA`w(s^mLNEuFPT0By`frNI})%$u+PCG?d&WQCLCr z>ko-s#n<@Jq8QOxv%#7GR_YeHC22*sJhQ+K^?&dE^hDIUwcNC0?#vTJDJo>n%T3!h z73)N*90117!mqieQ?BWhYua&fHO-w974iy~#YAw#?cV;_3$_~%*?JH%5X94wxBJom zrgooxIooB5G}8%cRnG52-+DnfA9jAPcb+&W1VBb6z%SeZ$qO^2#An}A?#y%t*j2U4WB}$xdHJXP@IHVmDhr>>lKA>@W5OYa% z1$T#21qI$DKNglJJzMM+8A?ii6k7-uFR*Agrv=3B6fsp$Y9}RJo3C>wVxqp!^LC(Z z*^*m;1aT+BIWXW^vN*2XBykWTxh$4V^lE#T@LX_MfDWOvfM&UXl%p;_pN{re@qbE^ zZS13DQYhB70Gn3e<5E`nVc*fFP#?77+gm2KY@y#q=xEiK+l;-p=mv6(N;$-?*XUz^ zCLU4-4hh@tTKSBZ;)4?`tl;(LbP4%VAt$`sS0`9SLecwtK#0>rKOa|FodT?>8CTL6 zii-Q}tVj8@GW9SwcZ1I8ZcI#4jZ_1n&8CfTj&$@0b$~ZxsxUv~vG$GF^jUDZ_h;i$ zd^LtLx6*gaTv#O=ct7uOn0`Ag%-ADY5+p$S7 z%Gji#voOE_7ZgquUdK8wU6EdDs;_8^Od5gn125efECs5pLe0<;ge_!1eTKEy96&Ov zgBHM0DU7-USD>M%DQ+unv8p6ak81czvVo-{a&?P&ni zM{Rl%Z>dpWq8{ymZr8dNCj9HPaHBub#F$Ny|H4g-xkh6rxHZ8eHStUb zQy?p$yts}zha`i{P?#VpgS41OOjHm>LR2pg9G>~+ArL;Wx8Q_&>m=rj#tY;A75H%Z z!I<_ZpxN_G*-KIF^WfM^QSI{pb`xkIK-Shnv!+L(5CyO#yT`L{zj_JG@l^`uItEW@ zBCmp^E6;KbJV0vV1etQJoM=w1!s)R^DGS-iUrt+8jZbNFlp5x*nTTdT)_W;lgn_(I zVB^NTrvQK-a#ok4Z);8J(L8E_9*@V zttCB_ghJq#qW62!rhrWZx?WI5IVUq?mS!wYX8wUJ4ec=|G`(X)Ql@T|Z^8K4OHkQc z%avVdp#s5-h26murFGc$rb51qUaG>crXpvIz1t^8n_^~msu3R=@MufQyas(O<+Nse9 zQ`5_&kV*eGbHpz^f#^y?MaQL#S*U0nJNjVq;8NG`$IvuQLKi+y1ym9gE(0s}A;k38 zX|#ngA=+MBxaLjr+&%IN*YWbPK&oDhM=`4fVVG~N#n3w_Cwfjw2yijJ`-h+BnM zzp=y13xLZcTGkT-~WOuu$kn9j?v`25X zkMexuEc3k=bRkPw0wPSJ2G1R#h5R+YJwW0&e@PjgoZf^BPiO{&ab+;iy3^^XvguOs z11ZVci^W589DrlO3&v-9_^NW1PUTCgtPB~xOx70fDj#@HK}WBp{bbP?;*(2YF7^6H3=jHlsU<3l1qF#k%|uS!c(6YUo^1OQ0r z?zx~)W0knzYz;7UdjhCbivN_n(d0x`4r*`^s$%@A%KYdDS0x{NQk8V+a#dF`*>Fm! znRZT`0I~rRMimQrgZ+35htY{!F&A3;iFQ|}(OI8^`pfTsT~1Vt_n=OW{L!5}6VTDP zt(h9Q&1(q$P!r4ePHe#_SO^97#vNRMv2ZPao;uF33IGY70URnh8Yv)W_9spR+RzRs zO7Ik{*G@OzIUlFlh{TVKCFwV2PMbpe))2Kb1#9e~oiZ*)p!I&9x)U+wF`xrz&F9*P zsG{MesV8@Y^(-qRr^zpRyvq-k$kK}=;gBa9PP`U`xVlO*!&}N;L~H;FIQZc?$*3v; z!^|0Az5pD-LfiylpVMJ>>Xj@hWDpZxh>=HWJtR z;9cW#MBmPjvHs`n#FDrZ;Y{me z=r4YO{-oPNhEU$O2k#hX1tt33efnX=!f6p`NXeUhaH3hz0t*;K{yS%{ED^svx<`F=aBw^`fs)ve6U^x*UFac*WBZzrrf%0d& zu_pS2LJ+m}j2W+3lYGv^AT~gNn4xGl(g#o(Ui3RXUBOyK zyhn#;Q`j5QekgqX!ap-9vFTt^CeNc>-07VA)I|;SR~d9;dJtZnIxKxht@-`GpwHVC!;<#y7L}Q5u%bvUQai##>!ava!V~0V!2}H2akQW$aDd5y!fvi$wxm@bH zp=5p@+1>sge4HSs=z~9$*QC*V$!6Rte&O|=8-ZWXQ;xb*M=}O26CTm89~l>bVjp6R zlKIm-AX5o^6cg1}hW#62Fz(3Dex9z#k*UK{#DQ6ML;Y9!(; zey%5$4=u#wAzDasS18}*++7T=`QDO-AmSujQ#KjL9-*Ci#i6~p1wmJHXwwlr>4qz? zyRkYlcVB~IkDGN$MM8=MOA@#^{1A?z3<3uf455#k0@HMaxd&`FV75O4b;jUieQDiQbqhFjv8<8kbQki(2%raJ}|oj%E< z%f4d;wugfR#H%ADU_sD5`zTHlTY}E{egfx5+jyPNT{S|X8vKb=%2*q``rquyv`8uiYGL64~-)o0C5b3-u2(F_vJ*$HmW}`vw`}6?tmQNE$C8yc zY+XZlF^pXYiTe=t@>pt6zF>Jb!dqdnd~G{lYY#N6!qoiHBodlPC?QWiRiN%3Q$|TY zWEEzLjFl<*DU?!7I6cFGJCvVJ3C0k82$2#^)_qJ0)>8qIDKw&txj~I8K-dsSA48QK zB4)iu<~z7C1(L-Eg&ZZgn``5V*}uaq@>*Fk2*yNS#89`iE3IK3tPsdY1^0@AYk{n) zt*m8A-o38lyBN5r%Bk?$D6=IGguH0XopWvX@Cfnt$<_xOEeD!dAq`&r+LW(x&%U-$K~Hv?)g~6-V9X6DWNw*jZM_jWPNls zzQxba(JEM61T94!e$;w6#rddqE0r!x0hh}iSypks$a=yAMcb||-}1pqbrodPw=2uH zZsJ|jy+V3*eBaN;)jYIt8hvH|ou>j|JHw~da-(NzV*Kb<^(Wv>jL?0S#eniz--e(g;^{O6l2^@XoLboh5lD?ds}T+g0lBY+cwi zI?%hNG&Ef7o4=trykUOxf<+6Otx#X@+66%a$@jPz_S0k6tRm+v;0*HsC^{kIF zsW?xD-zu&n>SM7~?6ID;9MH3SY3CB^ZlzPz{1%6YOG6{Qg9G8ZVsBrmXK~oy+Y_E~ zW;k5xTeqOEG|)JwyV%EzaLpy9;nBX4#fwJ=whk3HH_llTQmQx*u30uTv?knA>>Dk4 zUY0ubPh-w;Ri@S65sj(4ck_nQ5Tj~au+T=uY?X@rtGfq>O2B=a@8Eu$+TlvlW86=x zNblnQIIbh=OR+ROe|>j9ooV6!17MsE3f|?^KMx?N-6TE#p!8(Y(~q*Me5- z!n(fVmcgOnCY~;A6)N6BUtUccujIny^j7utc9+8b(czJBZ7CetPzr~O{iTqJUO%!S zEDqbF;?PiW`w>BBeQBUH)Z0DZY7&gv7PKD#BQ{nVja&@!Po`f9l3lb}$apZ4g=;Px z94N&|mSlZV_?V||;h|_xG+3FQ6sP$yd{sZDaM_&kTi`msv^}g@ad}w7ePg;{^~{TZ zoB0)<>r~?x`R^?9o!z#my`!^h@#$xr8JF@DuQ_oF{FK7fqmG_-%(2JSO?MfNpE2`< z6K93>4JXY$`IJ)|=bSco-uwkk%`L49Uu3f{Z?l@{M_?ayz=}DUUlI`D=)re z)uoqRe#MnnU0qz;-BVh(enaoZO?~|XgPX4%8Xg(lvUS_`>(qG%hq+ClfH-dV(DuzE zgCOnOG6hd_gQ)b*;7Zb>>5Ua>tX1!*iu5AxH&>*)xPNU$TKK>8p!5Z#j|kIWfc|Q$ z9Q~>7la92lp5FDP;StAj965>etuAivh2U3jDI(0)_K6e1`)GF^?Oo3``$*ee+c(&~ zY5v;Lb?c1#!tfmXjm6!*c#Q9$os+qe^j+MC73ueLe|$xnonKyEMOrfHMAAnzAHy4p ztEHUFR&RXchu434=vM=0EdI?!f4Ae3$rsLzQ1eM z^l$8abyDUQ@~TYks}KK5Wln$J*LJJi+-Gn9y~_Tw{e;yj|E(K-dcW$J)pyoh)wAg{ z(Qj4P_WCy*t@=XhIHK{r0B5~E=lCjWvdMSxF!fvfPnUSx1noKcJg?N(H#jeJ^jCjG zy?fCZdVKYt4?p+NmSkXpe@QyQza;&V;NOP+Vz=a9@?$M`4__XB>zi=@5eB{N_l!j*tNDeTxxFvx~(m-!j0O}J5XxsAMAm?dk02J z>q|r7Aj%4~?k3Emq#V za@i{=Cw=8$I)927=cCB5zwr%a&uGXG)Z6wyU_jE35>u4^ucC}-+727)2ty|mEgs2)S zV%kYyI@3Xla8vI{X~^W|IQ_ejx_^Vgf}6%Gj*OK0H;;rPgJDnamfoIHxORJZU1?~L zcb5+JVh{0Vc*9Wdz$V=l!#+$%aId?#x!B!1vi%HfT9dnfVn4CD2D`1JL&dGDx0Jf+ z*>!567tFxZLlc?=6!cVQ{t-F8?=91*(6d}?0M8JjJ`gX>0` z*7a^1)}%PY#al6@;Jt?4JjEprnWTSmQ2HjsnzX>=z0zk^q^D)%ahZ6pw4k@jY{zrV zq{M<99I`(HqkVnh=0UNNp>VUTW1DF~dZ^Sr3X|DV3P*<_$#t@Q2iI;ab&rHSr4fF} zx^+Wu_l9um;AmeDWLdPbqXWamb)^v2QiMgV4|@kfRd+<-J1i$O^`O|ddh_Ve=D}e+ z9HoS}+*l}h!rrZ1!ptPSjr%zjX=!c8R;2IYURqm{|6|;XTPEp;xEK7B^yA#msz__T zD$~C>DE(gtrDal8=C3{|ebhne8FBj015YPuzBjdi$Hw8AV3nlL=3cN$(!042E7EV| z{^W}EySNv3OY*<_p!8oHl>ST7OSqE!w;%NW9dY`i!%Dz`(f+j(k?X?l4Wk2_hJ}s2 z1Di+x?C9tnceKMt;iB;8&oh6H9##)y%k*wD&VL8}*h2d!a7j}!(jO@Hg(WeC#bHUT z0MZ^Ez}M8hK^9ghnpzw(x6=yVX_Q&f53vpR@(TZ%d@F+kecQtgMR-NC#6zcpF*0PN zWFYS=lF4RZUbq!cV>mEK7N>qVU(E2O)O{&+C;b!di0+c~JGmDhi?x=O#hxCxOPsoZ z`d4l5Upv@m(iN6y+J7CtlD0`&-lwF5d4S@?Qu_JFz@hP&gW(VkmHLal13jgoYj`KP zzU1>8Gq!EieUcH3 zg8vc0;sq?g-LxUCwx7Xdj%j&gU8efuL!@abq zB)yk=@x3JdB=@omyy$9ge7{$`&imAUuepo<`g6rY{iY@QJH5*D7k+HNf95au`)8%R z%kjD699&iG8;S8WBv&$5YJcU1Zp1pByL52yg293HmFZO*28Tw<58?_|BH_0V4)v@o z4i)={Usc?;Y+HA!)KhW~E|*WcoRb_E;wwEk{fg1eXe~vg|3#xCFw(f`a>Cj@$9!8$ z+uN4K&~tJj>AUoksBb;15hEM=sldv_6}_t1w{CE#ztj^qyqrWa0l+yMibIqQxstl1 zE6wIgyq<#lk!#B%Lrt5d2@VZt-hW+J@Ghd>hrqE@RR58zrbx@u93+b=ZLw0g+2(}i zR-Shbs+cCoc%+tWM#3$Qs+jy~KHl~JKH8Gic0}#H0KMK#TcX#Fc&ywW-qMNj?Se|dFoC|_T*@Xm zH;;RHYZq`;K9|R~nMj@9v$cg zA4S90Aqq@+7-W5AD85A?pmg<@`Ui)$j}DY&LusH#&)2c`hMh3Fc{5C^)HArbG=#^3 z^)-{iL!+B{h=DmO=@Ry_N{Lo681}*9dSk6YH4MO>^t`_~u$^3}ciltiCG&eqTeRw< zC*h#vl`{4Kb=mBtZP+yils z*`P?*21+9^icJPBw5x%hzRK?O4GwPB@X$JX5y=L=zTW=ck+89}VEuw{czeG=K>6kf z=jNN1Rxuz7Cw--By`%lEB4!Nz41tbAV0-T{#(w#^9#9q|~{_PW7QaJF%9*dcZ( z+)rp|*(_G_Nw4Hr^!q$C1$p&qk;m&J&kXWB9yq<*tv`pG5#Q#kLv5Cd?cG7Xqxrp$ zU$U0V?Mpd+O@-i)M5M_l*DLaUhx}9cZRG0Us^t>D>g3YOkUUG3&rc_P2A6yX2lsn; z`#^Wo;6S&WbhE>P>4;w=pY+ zFK?n9>7BQ6Nv=880UFVO;|=hQ*rIT-LmpE`det}hm0fvoD^{55pW(f1fy-h!aB#lO z#ev@LO-{+1R`14p7QmM-oTSert^S?GmB3c=RJ=IRJ(h4Eaf!nU_Di|W;i}B@3ewBs zXZ#r6Ea!KEmWD^WyCI5oqkY}Q0ij^o6N8ae?7nssa(7UDn7)RTJ$O7YtfsBS;r^xt z3kcpQv)Lxi)o{}U(4V5bW}VqiEi{xd8OZp zW~4jGc2QozM!JgX)HA(Pe$^@1Y5tA2k&%ybeUj^5uD|Bm#kGZN6W5hoi=fk&^O{Zl zM~QcE{l|#~?{ThgaE){QJ=dLFw{g9R>sqdBxL(C|HrG6^Dz0yyQ1Cv+br;v$xo+ax z!8OQrCD++pEnKs?3S2p^e`9Rt0*6FLI*vVdFcrf)pp6y0TaI0N*~q$1b7zu%C09b9=X0NgPrQgoi#bb8CwtZCh*c(Yewzfg zG?ujz1H+3gUIfh}L@t`utB?|xktd5!cJz2Pzrs1$4EiV8uu?ZX*FRWy_lDAN{h4Q; zd8oFxrV%ke5{5&)>o<(dSyL0@SzR}>I9#)4UX6RGdy^IKp+4c_Lvedw<5y$<4!?*C zZw=Ss@;pZ#^~);aUjM>VoXk9zWsh!`C;~G|sDewT&~G z_e;t5nq=b3uJ?8QBTe|?;Kie~AzCSMiFTT|H8(f6G`BV{Y;J2_)ZE_O(cIbG)zaM3 z($dMEM@wf*S8H=?OKWTE!q&FdMXl|v9j%?MT??BRwk&L2xNu?H!bJ<) z7j`V{T-ept+}6_8+P1K*t!+_Tds|0aXIs~z=0z=wS{E%`)V65RqV`1{i#iu|wKuo7 zw70e|Y;S8{)ZX6S(canK)zRG1($U(nu%oSGQAc}6M@MHzS7&o)OJ{56!p^qNMV;-P z9i5$>U0rmsi{`tix{G36JaY4$z~M5+b~)D-Tq8rHrFF%=;ZozALm*>o08L1{?hUpy z^R`nR4sD>ER>YF@4cyDunxyaIUbKOXm99Bb92$ucF+o}P25uc%8^U7eYYX?%hVb)0 z_-!cHA>G`e^6W8R35c+id#}l~LFvFwCwgp7!Td;)J>rK+T6oaT<@u>hrm8B)R^_tQQ)_1Cr`H}o zsW7>AO1h9b>ZqfukM)mB*ZI>^$7g5wGlN;jhN-!!`RtEqPPGKB{`-Ot1RqS_m-|1# zZ!^COexKT3egC%YH{bd<%~xD`^DTR3{_Er^=U?#K=N2@bea&lDKe_wXx8C;l4}9cP zpZ?O9zxuTwKlPLSUV7?Lb6VOu7N36RidVj7_gi`S(NBH)%U}EEqfh%FMaj#r+%_;($r-udP-xv z|Ni06e(qnsyYH8~-gxu9@BQrOzVwfe{@{n_-SL@keEFM?uDI}`E3SI&>YLvBwvT-L z6QBG1m;dp*Q;$9F)z|#zfBk;{SpT&@ethz*fx($4tbY9)KK#IrPd|L@aVO4Ne(r@A zU3v92uYJRgkA3Oe|MH`Kzx)l+RwJW-b;^RK_dW25&;8@0-+la@OWtuu^V??q)3?95 z|H6x|eswlCrEqG~FaCXCu;a|LmoD3L+p6`WU-{}|-}>J7fA;(RUbwp9hR4%4ydpOv zT{U&*hbE8Rmzh<)b4F@<&QCX`+tS&TpRLMHtywwcsO)9gRC;Djbt;$2rUI~`Hl0c3 ztNh8wWG>9k$X<~Rs*bB&nLZ~qKjo*VR!ym0oIc^?)nR{niDV~ewVr` zdu(-G^|ab)wHvEys*bO^Dmyo`yk=gyHtnZc^7GQiSLIVO-Y@R z?aa;1+^~OYU9M^B{8asv`YB_#q;Gh~^!zb5-kxd7oSqFP*Hw>wwqc}p>^sNTX2$ks z#vZTzk2_Nx)jO}5Hug|%?CY7Dy39My| z-FI*8ap{(O(mQ`}O13tW8GCPG=Wnupcv=;2Z%vPVHZ>zPWs+CLd5L~H$YirYE>|7Y zWb(n}bitn*9F;kG>NNkD;J9G=q?wr$aJpC;DOXff`@}ggKq`juKkzl?*!is ze&FxTJQ4gX{dDk)a9{e_;Qyq4>(`!o`dJrV^tQY1de7@_{)@l*o4^0mn?6#Nt!_W_ ztjmA(*tgQt>e@RlzhcJ+Km5RF7VSOi%{RUEt}-$T7cac1r*zH7KQUuwHdm9Mc3gYc z;`e{xd*83_*mK+avo)umwXXMVZ=X82`g6bd_p8_b`nmn9E`8^_7BrpOc-ftI-}ATk zzVH3_f9l~cR^@AtIbreH%PxNJ`yTnm-P!5KH=KOd**|;c-}is%%jxi>lTT?}*tz(; zS6;Ak)n%6p_ttcm)@>Tz_WB)fx%Yz)JUIT?haVUi{On)6w&8V|RC<1DUCM7-Fm}TU zsg@}-)3d8j%*@TaB0c%Eu@6?wPR~v^=GyWXp0l%~`q-LW-RaA^Qr)@g=3_JUsTmo6 zNoV@}OjEihTb*4Jo|>+$Zci=F9G^|sW>>E0SU726c0sOY=P8$*KR0*UvB#e>bJ}s$ z7t+8hCQZ-QRGpVQwR$wa^sLjWPS4a-U0mg73aQN4&1+9QFIO}6-q$uP%hyy*I(l(c zP5Zp`abur6qi0p^dDS(`m(4gYw`$UgY|YqzF0VNub?%Ce)Z|=ERadrVXZ!T*>8Y8Q z`%_vc?RwX`(frsK-*Q3sq}|PhW8e0{8_vDwlQ(o_PfK4@bxO_hn#Ro0H$3?2()sDG z?9?SfiFZ7k+x?x>s{iI^I~PvzPpF!l&h5PAru3%Fq*Qgb@b)$5R*#%9_MbJwxy{Ec zzfPoZMfLQtH}5<*^~R-Bj@i9(R#ny5zs${?Rqt<}pE^Dr>|8Qy>f(&Q^Rd%z`03bx z&AA|5lMZ%GeZ>W5j(y>bDnET$W=31EbMm}&PwnM3V;}B3VbZ*GH55@b_Rd}3OHWNr zN^MQAu7bX%)TTQbTw|``!kw4aoZsYEBx zy3M6S+oiIu($c@FnX8D*SGX_lD)U}Nx~$illW6tm=G7yErUtJ@pdOU}YSOni70mvZ zuGv_T*7}HKvy{SJ>Cn3^v&MVP(RX`C9T(234cE;2_jz~EJ*_#MH~8MY^Md!UnSbJM z*DUaUAGY7Me@*-E{3qJ|n);5}lb-0fZ*sA#sqUVx=9$Iwes$tK7c6NjuH3iro{KLU ztiR;Whwr(>d$hQ!^sReVc|WMX)Z6>SWgmXBc*QeM)nE14)Aw8zdcU}8pTFa3LRPch ze1v$wfBt#-=3@$e32qn!e)=T;#2K&7FRrfk>(YKT%p)^5bw=*AIzQ~7KspE4%hm)Z z_={CMog+(4a6G3Scfn=S0bJHUF-ZA&r88vkrv=BtgUC(YIX|1K2~OmcK%a9=tNli* zr`A*k7L^V1R;#*1qk+;hgD(1)v~q%fp08tOeLv@4><8J}+*&`V&SzHyGpNt^J0|61yOS+kK#&4{y5q&j&?!PMeh5ZsaC6Ux;uL-@XUXI!DYr(^zFQRTZQb0B|o0K>SONr7P5O zT@?)gbFfWK`5v>|mdWVOubSc^5Iz6w^u;{)76iw6z#yH;<$~;q>DxJ@Cf%CzC;P`{ z{3+Br)oRT2_;*w0%rp?l_Gi5{WBa^ae)Z;|!Jg4>oVP))52bt*AF!XkgynIsmREA= zlzQf`-JZ%=9Mj1y3)+bqn?E2k564bpIkqdr&{;UYnXulDIaOQnqmB+&5n)N@S~+#* duSF4OQS>O{0vB|)g^k^%VrO^v!k*SS{}+lS-`)TK literal 0 HcmV?d00001